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