diff --git a/app/dialog/CMakeLists.txt b/app/dialog/CMakeLists.txt index 442f760e0..e289fa48e 100644 --- a/app/dialog/CMakeLists.txt +++ b/app/dialog/CMakeLists.txt @@ -27,7 +27,6 @@ add_subdirectory(projectproperties) add_subdirectory(rendercancel) add_subdirectory(richtext) add_subdirectory(sequence) -add_subdirectory(speedduration) add_subdirectory(task) set(OLIVE_SOURCES diff --git a/app/dialog/speedduration/CMakeLists.txt b/app/dialog/speedduration/CMakeLists.txt deleted file mode 100644 index ee025e694..000000000 --- a/app/dialog/speedduration/CMakeLists.txt +++ /dev/null @@ -1,22 +0,0 @@ -# Olive - Non-Linear Video Editor -# Copyright (C) 2019 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 . - -set(OLIVE_SOURCES - ${OLIVE_SOURCES} - dialog/speedduration/speedduration.h - dialog/speedduration/speedduration.cpp - PARENT_SCOPE -) diff --git a/app/dialog/speedduration/speedduration.cpp b/app/dialog/speedduration/speedduration.cpp deleted file mode 100644 index 43aea4b21..000000000 --- a/app/dialog/speedduration/speedduration.cpp +++ /dev/null @@ -1,354 +0,0 @@ -/*** - - Olive - Non-Linear Video Editor - Copyright (C) 2019 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 "speedduration.h" - -#include -#include -#include -#include -#include -#include - -#include "core.h" -#include "common/timecodefunctions.h" -#include "widget/nodeview/nodeviewundo.h" -#include "widget/timelinewidget/undo/undo.h" - -OLIVE_NAMESPACE_ENTER - -SpeedDurationDialog::SpeedDurationDialog(const rational& timebase, const QList &clips, QWidget *parent) : - QDialog(parent), - clips_(clips), - timebase_(timebase) -{ - setWindowTitle(tr("Speed/Duration")); - - QVBoxLayout* layout = new QVBoxLayout(this); - - { - // Create groupbox for the speed/duration - QGroupBox* speed_groupbox = new QGroupBox(tr("Speed/Duration")); - layout->addWidget(speed_groupbox); - QGridLayout* speed_layout = new QGridLayout(speed_groupbox); - - int row = 0; - - // For any other clips that are selected, determine if they share speeds and lengths. If they don't, the UI can't - // show them all as having the same parameters - bool same_speed = true; - bool same_duration = true; - bool all_reversed = true; - - for (int i=1;ispeed()) != qAbs(this_clip->speed())) { - same_speed = false; - } - - // Check if the durations are different - if (same_duration - && prev_clip->length() != this_clip->length()) { - same_duration = false; - } - - // Check if all are reversed - if (all_reversed - && prev_clip->is_reversed() != this_clip->is_reversed()) { - all_reversed = false; - } - - // If we've already determined both are different, no need to continue - if (!same_speed - && !same_duration - && !all_reversed) { - break; - } - } - - speed_layout->addWidget(new QLabel(tr("Speed:")), row, 0); - - // Create "Speed" slider - speed_slider_ = new FloatSlider(); - speed_slider_->SetMinimum(0); - speed_slider_->SetDisplayType(FloatSlider::kPercentage); - speed_slider_->SetDefaultValue(1); - speed_layout->addWidget(speed_slider_, row, 1); - - if (same_speed) { - // All clips share the same speed so we can show the value - speed_slider_->SetValue(qAbs(clips_.first()->speed().toDouble())); - } else { - // Else, we show an invalid initial state - speed_slider_->SetTristate(); - } - - row++; - - speed_layout->addWidget(new QLabel(tr("Duration:")), row, 0); - - // Create "Duration" slider - duration_slider_ = new TimeSlider(); - duration_slider_->SetTimebase(timebase_); - duration_slider_->SetMinimum(1); - speed_layout->addWidget(duration_slider_, row, 1); - - // Calculate duration that would occur if the speed was 100% - duration_slider_->SetDefaultValue(GetUnadjustedLengthTimestamp(clips_.first())); - - if (same_duration) { - duration_slider_->SetValue(Timecode::time_to_timestamp(clips_.first()->length(), timebase_)); - } else { - duration_slider_->SetTristate(); - } - - row++; - - link_speed_and_duration_ = new QCheckBox(tr("Link Speed and Duration")); - link_speed_and_duration_->setChecked(true); - speed_layout->addWidget(link_speed_and_duration_, row, 0, 1, 2); - - // Pick up when the speed or duration slider changes so we can programmatically link them - connect(speed_slider_, &FloatSlider::ValueChanged, this, &SpeedDurationDialog::SpeedChanged); - connect(duration_slider_, &TimeSlider::ValueChanged, this, &SpeedDurationDialog::DurationChanged); - - reverse_speed_checkbox_ = new QCheckBox(tr("Reverse Speed")); - if (all_reversed) { - reverse_speed_checkbox_->setChecked(clips_.first()->is_reversed()); - } else { - reverse_speed_checkbox_->setTristate(); - } - layout->addWidget(reverse_speed_checkbox_); - } - - maintain_audio_pitch_checkbox_ = new QCheckBox(tr("Maintain Audio Pitch")); - layout->addWidget(maintain_audio_pitch_checkbox_); - - ripple_clips_checkbox_ = new QCheckBox(tr("Ripple Clips")); - layout->addWidget(ripple_clips_checkbox_); - - QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); - buttons->setCenterButtons(true); - layout->addWidget(buttons); - connect(buttons, &QDialogButtonBox::accepted, this, &SpeedDurationDialog::accept); - connect(buttons, &QDialogButtonBox::rejected, this, &SpeedDurationDialog::reject); -} - -void SpeedDurationDialog::accept() -{ - QUndoCommand* command = new QUndoCommand(); - - bool change_duration = !duration_slider_->IsTristate() || (!speed_slider_->IsTristate() && link_speed_and_duration_->isChecked()); - bool change_speed = !speed_slider_->IsTristate() || (!duration_slider_->IsTristate() && link_speed_and_duration_->isChecked()); - - foreach (ClipBlock* clip, clips_) { - double new_speed = speed_slider_->GetValue(); - - if (change_duration) { - // Change the duration - int64_t current_duration = Timecode::time_to_timestamp(clip->length(), timebase_); - int64_t new_duration = current_duration; - - // Check if we're getting the duration value directly from the slider or calculating it from the speed - if (duration_slider_->IsTristate()) { - // Calculate duration from speed - new_duration = GetAdjustedDuration(clip, new_speed); - } else { - // Get duration directly from slider - new_duration = duration_slider_->GetValue(); - - // Check if we're calculating the speed from this duration - if (speed_slider_->IsTristate() && change_speed) { - // If we're here, the duration is going to override the speed - new_speed = GetAdjustedSpeed(clip, new_duration); - } - } - - if (new_duration != current_duration) { - // Calculate new clip length - rational new_clip_length = Timecode::timestamp_to_time(new_duration, timebase_); - - if (ripple_clips_checkbox_->isChecked()) { - - // FIXME: Make this a REAL ripple... - new BlockResizeCommand(clip, new_clip_length, command); - - } else { - - // If "ripple clips" isn't checked, we may be limited to how much we can change the length - Block* next_block = clip->next(); - if (next_block) { - if (new_clip_length > clip->length()) { - if (next_block->type() == Block::kGap) { - // Check if next clip is a gap, and if so we can take it all up - new_clip_length = qMin(next_block->out(), clip->in() + new_clip_length); - } else { - // Otherwise we can't extend any further - new_clip_length = clip->length(); - } - } - } - - if (new_clip_length != clip->length()) { - new BlockTrimCommand(TrackOutput::TrackFromBlock(clip), clip, new_clip_length, Timeline::kTrimOut, command); - } - - } - } - } - - if (change_speed) { - rational new_block_speed = rational::fromDouble(new_speed); - - if (clip->is_reversed()) { - new_block_speed = -new_block_speed; - } - - // Change the speed - new BlockSetSpeedCommand(clip, new_block_speed, command); - } - - if (!reverse_speed_checkbox_->isTristate() - && clip->is_reversed() != reverse_speed_checkbox_->isChecked()) { - new BlockReverseCommand(clip, command); - } - } - - Core::instance()->undo_stack()->pushIfHasChildren(command); - - QDialog::accept(); -} - -double SpeedDurationDialog::GetUnadjustedLengthTimestamp(ClipBlock *clip) const -{ - double duration = static_cast(Timecode::time_to_timestamp(clip->length(), timebase_)); - - // Convert duration to non-speed adjusted duration - duration *= qAbs(clip->speed().toDouble()); - - return duration; -} - -int64_t SpeedDurationDialog::GetAdjustedDuration(ClipBlock *clip, const double &new_speed) const -{ - double duration = GetUnadjustedLengthTimestamp(clip); - - // Re-adjust by new speed - duration /= new_speed; - - // Return rounded time - return qRound64(duration); -} - -double SpeedDurationDialog::GetAdjustedSpeed(ClipBlock *clip, const int64_t &new_duration) const -{ - double duration = GetUnadjustedLengthTimestamp(clip); - - // Create a fraction of the original duration over the new duration - duration /= static_cast(new_duration); - - return duration; -} - -void SpeedDurationDialog::SpeedChanged() -{ - if (link_speed_and_duration_->isChecked()) { - double new_speed = speed_slider_->GetValue(); - - if (qIsNull(new_speed)) { - // A speed of 0 is considered a still frame. Since we can't divide by zero and a still frame could be any length, - // we don't bother updating the - return; - } - - bool same_durations = true; - int64_t new_duration = GetAdjustedDuration(clips_.first(), new_speed); - - for (int i=1;iSetValue(new_duration); - } else { - duration_slider_->SetTristate(); - } - } -} - -void SpeedDurationDialog::DurationChanged() -{ - if (link_speed_and_duration_->isChecked()) { - int64_t new_duration = duration_slider_->GetValue(); - - bool same_speeds = true; - double new_speed = GetAdjustedSpeed(clips_.first(), new_duration); - - for (int i=1;iSetValue(new_speed); - } else { - speed_slider_->SetTristate(); - } - } -} - -BlockReverseCommand::BlockReverseCommand(Block *block, QUndoCommand *parent) : - UndoCommand(parent), - block_(block) -{ -} - -Project *BlockReverseCommand::GetRelevantProject() const -{ - return static_cast(block_->parent())->project(); -} - -void BlockReverseCommand::redo_internal() -{ - block_->set_media_in(block_->media_out()); - block_->set_speed(-block_->speed()); -} - -void BlockReverseCommand::undo_internal() -{ - redo_internal(); -} - -OLIVE_NAMESPACE_EXIT diff --git a/app/dialog/speedduration/speedduration.h b/app/dialog/speedduration/speedduration.h deleted file mode 100644 index 28a6badc1..000000000 --- a/app/dialog/speedduration/speedduration.h +++ /dev/null @@ -1,86 +0,0 @@ -/*** - - Olive - Non-Linear Video Editor - Copyright (C) 2019 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 . - -***/ - -#ifndef SPEEDDURATIONDIALOG_H -#define SPEEDDURATIONDIALOG_H - -#include -#include - -#include "node/block/clip/clip.h" -#include "node/output/track/track.h" -#include "widget/slider/floatslider.h" -#include "widget/slider/timeslider.h" -#include "undo/undocommand.h" - -OLIVE_NAMESPACE_ENTER - -class SpeedDurationDialog : public QDialog -{ - Q_OBJECT -public: - SpeedDurationDialog(const rational& timebase, const QList& clips, QWidget* parent = nullptr); - -public slots: - virtual void accept() override; - -private: - double GetUnadjustedLengthTimestamp(ClipBlock* clip) const; - - int64_t GetAdjustedDuration(ClipBlock* clip, const double& new_speed) const; - - double GetAdjustedSpeed(ClipBlock* clip, const int64_t& new_duration) const; - - QList clips_; - - FloatSlider* speed_slider_; - TimeSlider* duration_slider_; - - rational timebase_; - - QCheckBox* link_speed_and_duration_; - QCheckBox* reverse_speed_checkbox_; - QCheckBox* maintain_audio_pitch_checkbox_; - QCheckBox* ripple_clips_checkbox_; - -private slots: - void SpeedChanged(); - - void DurationChanged(); -}; - -class BlockReverseCommand : public UndoCommand { -public: - BlockReverseCommand(Block* block, QUndoCommand* parent = nullptr); - - virtual Project* GetRelevantProject() const override; - -protected: - virtual void redo_internal() override; - virtual void undo_internal() override; - -private: - Block* block_; - -}; - -OLIVE_NAMESPACE_EXIT - -#endif // SPEEDDURATIONDIALOG_H diff --git a/app/node/block/block.cpp b/app/node/block/block.cpp index 222bc042d..86daeca9d 100644 --- a/app/node/block/block.cpp +++ b/app/node/block/block.cpp @@ -49,10 +49,8 @@ Block::Block() : enabled_input_->set_standard_value(true); AddInput(enabled_input_); - speed_input_ = new NodeInput("speed_in", NodeParam::kRational); - speed_input_->set_standard_value(QVariant::fromValue(rational(1))); - speed_input_->set_connectable(false); - speed_input_->set_is_keyframable(false); + speed_input_ = new NodeInput("speed_in", NodeParam::kFloat); + speed_input_->set_standard_value(1.0); AddInput(speed_input_); // A block's length must be greater than 0 @@ -113,7 +111,7 @@ void Block::set_length_and_media_in(const rational &length) } // Calculate media_in adjustment - set_media_in(media_in() + (this->length() - length) * speed()); + set_media_in(SequenceToMediaTime(in() + (this->length() - length))); rational old_length = this->length(); @@ -153,31 +151,6 @@ void Block::set_media_in(const rational &media_in) media_in_input_->set_standard_value(QVariant::fromValue(media_in)); } -rational Block::media_out() const -{ - return media_in() + length() * speed(); -} - -rational Block::speed() const -{ - return speed_input_->get_standard_value().value(); -} - -void Block::set_speed(const rational &speed) -{ - speed_input_->set_standard_value(QVariant::fromValue(speed)); -} - -bool Block::is_still() const -{ - return speed() == 0; -} - -bool Block::is_reversed() const -{ - return speed() < 0; -} - bool Block::is_enabled() const { return enabled_input_->get_standard_value().toBool(); @@ -197,7 +170,24 @@ rational Block::SequenceToMediaTime(const rational &sequence_time) const return sequence_time; } - return (sequence_time - in()) * speed() + media_in(); + rational local_time = sequence_time - in(); + + // FIXME: Doesn't handle reversing + if (speed_input_->is_keyframing() || speed_input_->is_connected()) { + // FIXME: We'll need to calculate the speed hoo boy + } else { + double speed_value = speed_input_->get_standard_value().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 @@ -207,7 +197,24 @@ rational Block::MediaToSequenceTime(const rational &media_time) const return media_time; } - return (media_time - media_in()) / speed() + in(); + rational sequence_time = media_time - media_in(); + + // FIXME: Doesn't handle reversing + if (speed_input_->is_keyframing() || speed_input_->is_connected()) { + // FIXME: We'll need to calculate the speed hoo boy + } else { + double speed_value = speed_input_->get_standard_value().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 + in(); } void Block::LoadInternal(QXmlStreamReader *reader, XMLNodeData &xml_node_data) diff --git a/app/node/block/block.h b/app/node/block/block.h index 09182e7ea..7fa036302 100644 --- a/app/node/block/block.h +++ b/app/node/block/block.h @@ -62,13 +62,6 @@ public: rational media_in() const; void set_media_in(const rational& media_in); - rational media_out() const; - - rational speed() const; - void set_speed(const rational& speed); - bool is_still() const; - bool is_reversed() const; - bool is_enabled() const; void set_enabled(bool e); diff --git a/app/render/backend/renderworker.cpp b/app/render/backend/renderworker.cpp index 1f7902190..14cb5304b 100644 --- a/app/render/backend/renderworker.cpp +++ b/app/render/backend/renderworker.cpp @@ -189,16 +189,19 @@ NodeValueTable RenderWorker::GenerateBlockTable(const TrackOutput *track, const continue; } - // Stretch samples here - rational abs_speed = qAbs(b->speed()); + // FIXME: Doesn't handle reversing + if (b->speed_input()->is_keyframing() || b->speed_input()->is_connected()) { + // FIXME: We'll need to calculate the speed hoo boy + } else { + double speed_value = b->speed_input()->get_standard_value().toDouble(); - if (abs_speed != 1) { - samples_from_this_block->speed(abs_speed.toDouble()); - } - - if (b->is_reversed()) { - // Reverse the audio buffer - samples_from_this_block->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); + } } int copy_length = qMin(max_dest_sz, samples_from_this_block->sample_count()); diff --git a/app/widget/timelinewidget/timelinewidget.cpp b/app/widget/timelinewidget/timelinewidget.cpp index 07b74ca46..dcc58a82c 100644 --- a/app/widget/timelinewidget/timelinewidget.cpp +++ b/app/widget/timelinewidget/timelinewidget.cpp @@ -29,7 +29,6 @@ #include "common/range.h" #include "common/timecodefunctions.h" #include "dialog/sequence/sequence.h" -#include "dialog/speedduration/speedduration.h" #include "node/block/transition/transition.h" #include "tool/tool.h" #include "trackview/trackview.h" @@ -1070,11 +1069,6 @@ void TimelineWidget::ShowContextMenu() menu.addSeparator(); - QAction* speed_duration_action = menu.addAction(tr("Speed/Duration")); - connect(speed_duration_action, &QAction::triggered, this, &TimelineWidget::ShowSpeedDurationDialog); - - menu.addSeparator(); - QAction* properties_action = menu.addAction(tr("Properties")); connect(properties_action, &QAction::triggered, this, [this](){ QList block_items = GetSelectedBlocks(); @@ -1104,26 +1098,6 @@ void TimelineWidget::ShowContextMenu() menu.exec(QCursor::pos()); } -void TimelineWidget::ShowSpeedDurationDialog() -{ - QList selected = GetSelectedBlocks(); - QList selected_clips; - - foreach (TimelineViewBlockItem* item, selected) { - if (item->block()->type() == Block::kClip) { - selected_clips.append(static_cast(item->block())); - } - } - - if (selected_clips.isEmpty()) { - // SpeedDurationDialog expects at least one clip - return; - } - - SpeedDurationDialog speed_diag(timebase(), selected_clips, this); - speed_diag.exec(); -} - void TimelineWidget::DeferredScrollAction() { scrollbar()->setValue(deferred_scroll_value_); diff --git a/app/widget/timelinewidget/timelinewidget.h b/app/widget/timelinewidget/timelinewidget.h index cab78ee0f..2602aa97c 100644 --- a/app/widget/timelinewidget/timelinewidget.h +++ b/app/widget/timelinewidget/timelinewidget.h @@ -558,8 +558,6 @@ private slots: void ShowContextMenu(); - void ShowSpeedDurationDialog(); - void DeferredScrollAction(); void ShowSequenceDialog(); diff --git a/app/widget/timelinewidget/undo/undo.cpp b/app/widget/timelinewidget/undo/undo.cpp index d3603911e..54ff59ea7 100644 --- a/app/widget/timelinewidget/undo/undo.cpp +++ b/app/widget/timelinewidget/undo/undo.cpp @@ -596,29 +596,6 @@ Project *BlockSplitPreservingLinksCommand::GetRelevantProject() const return static_cast(blocks_.first()->parent())->project(); } -BlockSetSpeedCommand::BlockSetSpeedCommand(Block *block, const rational &new_speed, QUndoCommand *parent) : - UndoCommand(parent), - block_(block), - old_speed_(block->speed()), - new_speed_(new_speed) -{ -} - -Project *BlockSetSpeedCommand::GetRelevantProject() const -{ - return static_cast(block_->parent())->project(); -} - -void BlockSetSpeedCommand::redo_internal() -{ - block_->set_speed(new_speed_); -} - -void BlockSetSpeedCommand::undo_internal() -{ - block_->set_speed(old_speed_); -} - TimelineRippleDeleteGapsAtRegionsCommand::TimelineRippleDeleteGapsAtRegionsCommand(ViewerOutput *vo, const TimeRangeList ®ions, QUndoCommand *parent) : UndoCommand(parent), timeline_(vo), diff --git a/app/widget/timelinewidget/undo/undo.h b/app/widget/timelinewidget/undo/undo.h index 4193bb66d..88a7553e8 100644 --- a/app/widget/timelinewidget/undo/undo.h +++ b/app/widget/timelinewidget/undo/undo.h @@ -113,23 +113,6 @@ private: rational new_media_in_; }; -class BlockSetSpeedCommand : public UndoCommand { -public: - BlockSetSpeedCommand(Block* block, const rational& new_speed, QUndoCommand* parent = nullptr); - - virtual Project* GetRelevantProject() const override; - -protected: - virtual void redo_internal() override; - virtual void undo_internal() override; - -private: - Block* block_; - - rational old_speed_; - rational new_speed_; -}; - class TrackRippleRemoveBlockCommand : public UndoCommand { public: TrackRippleRemoveBlockCommand(TrackOutput* track, Block* block, QUndoCommand* parent = nullptr);