/*** 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 "speeddialog.h" #include #include #include #include #include #include "timeline/sequence.h" #include "project/footage.h" #include "rendering/renderfunctions.h" #include "panels/panels.h" #include "panels/timeline.h" #include "undo/undo.h" #include "undo/undostack.h" #include "effects/effect.h" #include "project/media.h" SpeedDialog::SpeedDialog(QWidget *parent, QVector clips) : QDialog(parent) { setWindowTitle(tr("Speed/Duration")); clips_ = clips; QVBoxLayout* main_layout = new QVBoxLayout(this); QGridLayout* grid = new QGridLayout(); grid->setSpacing(6); grid->addWidget(new QLabel(tr("Speed:"), this), 0, 0); percent = new LabelSlider(this); percent->SetDecimalPlaces(2); percent->SetDisplayType(LabelSlider::Percent); percent->SetDefault(1); grid->addWidget(percent, 0, 1); grid->addWidget(new QLabel(tr("Frame Rate:"), this), 1, 0); frame_rate = new LabelSlider(this); frame_rate->SetDecimalPlaces(3); grid->addWidget(frame_rate, 1, 1); grid->addWidget(new QLabel(tr("Duration:"), this), 2, 0); duration = new LabelSlider(this); duration->SetDisplayType(LabelSlider::FrameNumber); duration->SetFrameRate(olive::ActiveSequence->frame_rate); grid->addWidget(duration, 2, 1); main_layout->addLayout(grid); reverse = new QCheckBox(tr("Reverse"), this); maintain_pitch = new QCheckBox(tr("Maintain Audio Pitch"), this); ripple = new QCheckBox(tr("Ripple Changes"), this); main_layout->addWidget(reverse); main_layout->addWidget(maintain_pitch); main_layout->addWidget(ripple); QDialogButtonBox* buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this); buttonBox->setCenterButtons(true); main_layout->addWidget(buttonBox); connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); connect(percent, SIGNAL(valueChanged(double)), this, SLOT(percent_update())); connect(frame_rate, SIGNAL(valueChanged(double)), this, SLOT(frame_rate_update())); connect(duration, SIGNAL(valueChanged(double)), this, SLOT(duration_update())); } int SpeedDialog::exec() { bool enable_frame_rate = false; bool multiple_audio = false; maintain_pitch->setEnabled(false); double default_frame_rate = qSNaN(); double current_frame_rate = qSNaN(); double current_percent = qSNaN(); long default_length = -1; long current_length = -1; for (int i=0;ispeed().value; if (c->track() < 0) { bool process_video = true; if (c->media() != nullptr && c->media()->get_type() == MEDIA_TYPE_FOOTAGE) { FootageStream* ms = c->media_stream(); if (ms != nullptr && ms->infinite_length) { process_video = false; } } if (process_video) { double media_frame_rate = c->media_frame_rate(); // get "default" frame rate" if (enable_frame_rate) { // check if frame rate is equal to default if (!qIsNaN(default_frame_rate) && !qFuzzyCompare(media_frame_rate, default_frame_rate)) { default_frame_rate = qSNaN(); } if (!qIsNaN(current_frame_rate) && !qFuzzyCompare(media_frame_rate*c->speed().value, current_frame_rate)) { current_frame_rate = qSNaN(); } } else { default_frame_rate = media_frame_rate; current_frame_rate = media_frame_rate*c->speed().value; } enable_frame_rate = true; } } else { maintain_pitch->setEnabled(true); if (!multiple_audio) { maintain_pitch->setChecked(c->speed().maintain_audio_pitch); multiple_audio = true; } else if (!maintain_pitch->isTristate() && maintain_pitch->isChecked() != c->speed().maintain_audio_pitch) { maintain_pitch->setCheckState(Qt::PartiallyChecked); maintain_pitch->setTristate(true); } } if (i == 0) { reverse->setChecked(c->reversed()); } else if (c->reversed() != reverse->isChecked()) { reverse->setTristate(true); reverse->setCheckState(Qt::PartiallyChecked); } // get default length long clip_default_length = qRound(c->length() * clip_percent); if (i == 0) { current_length = c->length(); default_length = clip_default_length; current_percent = clip_percent; } else { if (current_length != -1 && c->length() != current_length) { current_length = -1; } if (default_length != -1 && clip_default_length != default_length) { default_length = -1; } if (!qIsNaN(current_percent) && !qFuzzyCompare(clip_percent, current_percent)) { current_percent = qSNaN(); } } } frame_rate->SetMinimum(1); percent->SetMinimum(0.0001); duration->SetMinimum(1); frame_rate->setEnabled(enable_frame_rate); frame_rate->SetDefault(default_frame_rate); frame_rate->SetValue(current_frame_rate); percent->SetValue(current_percent); duration->SetDefault(default_length); duration->SetValue((current_length == -1) ? qSNaN() : current_length); return QDialog::exec(); } void SpeedDialog::percent_update() { bool got_fr = false; double fr_val = qSNaN(); long len_val = -1; for (int i=0;iisEnabled() && c->track() < 0) { double clip_fr = c->media_frame_rate() * percent->value(); if (got_fr) { if (!qIsNaN(fr_val) && !qFuzzyCompare(fr_val, clip_fr)) { fr_val = qSNaN(); } } else { fr_val = clip_fr; got_fr = true; } } // get duration long clip_default_length = qRound(c->length() * c->speed().value); long new_clip_length = qRound(clip_default_length / percent->value()); if (i == 0) { len_val = new_clip_length; } else if (len_val > -1 && len_val != new_clip_length) { len_val = -1; } } frame_rate->SetValue(fr_val); duration->SetValue((len_val == -1) ? qSNaN() : len_val); } void SpeedDialog::duration_update() { double pc_val = qSNaN(); bool got_fr = false; double fr_val = qSNaN(); for (int i=0;ilength() * c->speed().value); double clip_pc = clip_default_length / duration->value(); if (i == 0) { pc_val = clip_pc; } else if (!qIsNaN(pc_val) && !qFuzzyCompare(clip_pc, pc_val)) { pc_val = qSNaN(); } // get frame rate if (frame_rate->isEnabled() && c->track() < 0) { double clip_fr = c->media_frame_rate() * clip_pc; if (got_fr) { if (!qIsNaN(fr_val) && !qFuzzyCompare(fr_val, clip_fr)) { fr_val = qSNaN(); } } else { fr_val = clip_fr; got_fr = true; } } } frame_rate->SetValue(fr_val); percent->SetValue(pc_val); } void SpeedDialog::frame_rate_update() { /*double fr = (frame_rate->value()); double pc = (fr / default_frame_rate); percent->set_value(pc, false); duration->set_value(default_length / pc, false);*/ double old_pc_val = qSNaN(); bool got_pc_val = false; double pc_val = qSNaN(); bool got_len_val = false; long len_val = -1; // analyze video clips for (int i=0;ispeed().value; } else if (!qIsNaN(old_pc_val) && !qFuzzyCompare(c->speed().value, old_pc_val)) { old_pc_val = qSNaN(); } if (c->track() < 0) { // what would the new speed be based on this frame rate double new_clip_speed = frame_rate->value() / c->media_frame_rate(); if (!got_pc_val) { pc_val = new_clip_speed; got_pc_val = true; } else if (!qIsNaN(pc_val) && !qFuzzyCompare(pc_val, new_clip_speed)) { pc_val = qSNaN(); } // what would be the new length based on this speed long new_clip_len = (c->length() * c->speed().value) / new_clip_speed; if (!got_len_val) { len_val = new_clip_len; got_len_val = true; } else if (len_val > -1 && new_clip_len != len_val) { len_val = -1; } } } // analyze audio clips for (int i=0;itrack() >= 0) { long new_clip_len = (qIsNaN(old_pc_val) || qIsNaN(pc_val)) ? c->length() : qRound((c->length() * c->speed().value) / pc_val); if (len_val > -1 && new_clip_len != len_val) { len_val = -1; break; } } } percent->SetValue(pc_val); duration->SetValue((len_val == -1) ? qSNaN() : len_val); } void set_speed(ComboAction* ca, Clip* c, double speed, bool ripple, long& ep, long& lr) { panel_timeline->deselect_area(c->timeline_in(), c->timeline_out(), c->track()); long proposed_out = c->timeline_out(); double multiplier = (c->speed().value / speed); proposed_out = qRound(c->timeline_in() + (c->length() * multiplier)); ca->append(new SetSpeedAction(c, speed)); if (!ripple && proposed_out > c->timeline_out()) { for (int i=0;isequence->clips.size();i++) { ClipPtr compare = c->sequence->clips.at(i); if (compare != nullptr && compare->track() == c->track() && compare->timeline_in() >= c->timeline_out() && compare->timeline_in() < proposed_out) { proposed_out = compare->timeline_in(); } } } ep = qMin(ep, c->timeline_out()); lr = qMax(lr, proposed_out - c->timeline_out()); c->move(ca, c->timeline_in(), proposed_out, qRound(c->clip_in() * multiplier), c->track()); c->refactor_frame_rate(ca, multiplier, false); Selection sel; sel.in = c->timeline_in(); sel.out = proposed_out; sel.track = c->track(); olive::ActiveSequence->selections.append(sel); } void SpeedDialog::accept() { ComboAction* ca = new ComboAction(); // undoable action for setting "maintain audio pitch" SetClipProperty* audio_pitch_action = new SetClipProperty(kSetClipPropertyMaintainAudioPitch); // undoable action for setting "reversed" SetClipProperty* reversed_action = new SetClipProperty(kSetClipPropertyReversed); // undoable action for restoring clip selections SetSelectionsCommand* sel_command = new SetSelectionsCommand(olive::ActiveSequence.get()); sel_command->old_data = olive::ActiveSequence->selections; // variables used to calculate ripples long earliest_point = LONG_MAX; long longest_ripple = LONG_MIN; for (int i=0;iIsOpen()) { c->Close(true); } // set maintain audio pitch if the user made a selection if (c->track() >= 0 && maintain_pitch->checkState() != Qt::PartiallyChecked && c->speed().maintain_audio_pitch != maintain_pitch->isChecked()) { audio_pitch_action->AddSetting(c, maintain_pitch->isChecked()); } // set reverse setting if the user made a selection if (reverse->checkState() != Qt::PartiallyChecked && c->reversed() != reverse->isChecked()) { long new_clip_in = (c->media_length() - (c->length() + c->clip_in())); c->move(ca, c->timeline_in(), c->timeline_out(), new_clip_in, c->track()); c->set_clip_in(new_clip_in); reversed_action->AddSetting(c, reverse->isChecked()); } } // setting the actual speed if (!qIsNaN(percent->value())) { // if we have a percentage value, use that on all the clips for (int i=0;ivalue(), ripple->isChecked(), earliest_point, longest_ripple); } } else if (!qIsNaN(frame_rate->value())) { // if the user changed the speed by changing the frame rate, bool can_change_all = true; double cached_speed = clips_.first()->speed().value; double cached_fr = qSNaN(); // see if we can use the frame rate to change all the speeds for (int i=0;i 0 && !qFuzzyCompare(cached_speed, c->speed().value)) { can_change_all = false; } if (c->track() < 0) { if (qIsNaN(cached_fr)) { cached_fr = c->media_frame_rate(); } else if (!qFuzzyCompare(cached_fr, c->media_frame_rate())) { can_change_all = false; break; } } } // make changes for (int i=0;itrack() < 0) { set_speed(ca, c, frame_rate->value() / c->media_frame_rate(), ripple->isChecked(), earliest_point, longest_ripple); } else if (can_change_all) { set_speed(ca, c, frame_rate->value() / cached_fr, ripple->isChecked(), earliest_point, longest_ripple); } } } else if (!qIsNaN(duration->value())) { // simply set duration for (int i=0;ilength() * c->speed().value) / duration->value(), ripple->isChecked(), earliest_point, longest_ripple); } } if (ripple->isChecked()) { ripple_clips(ca, clips_.at(0)->sequence, earliest_point, longest_ripple); } sel_command->new_data = olive::ActiveSequence->selections; ca->append(sel_command); ca->append(reversed_action); ca->append(audio_pitch_action); olive::UndoStack.push(ca); update_ui(true); QDialog::accept(); }