Files
oak-editor/dialogs/speeddialog.cpp
T

412 lines
12 KiB
C++

#include "speeddialog.h"
#include <QHBoxLayout>
#include <QGridLayout>
#include <QLabel>
#include <QCheckBox>
#include <QPushButton>
#include <QDialogButtonBox>
#include "ui/labelslider.h"
#include "project/clip.h"
#include "project/sequence.h"
#include "project/footage.h"
#include "playback/playback.h"
#include "panels/panels.h"
#include "panels/timeline.h"
#include "project/undo.h"
#include "project/effect.h"
#include "project/media.h"
SpeedDialog::SpeedDialog(QWidget *parent) : QDialog(parent) {
QVBoxLayout* main_layout = new QVBoxLayout();
setLayout(main_layout);
QGridLayout* grid = new QGridLayout();
grid->setSpacing(6);
grid->addWidget(new QLabel("Speed:"), 0, 0);
percent = new LabelSlider();
percent->decimal_places = 2;
percent->set_display_type(LABELSLIDER_PERCENT);
percent->set_default_value(1);
grid->addWidget(percent, 0, 1);
grid->addWidget(new QLabel("Frame Rate:"), 1, 0);
frame_rate = new LabelSlider();
frame_rate->decimal_places = 3;
grid->addWidget(frame_rate, 1, 1);
grid->addWidget(new QLabel("Duration:"), 2, 0);
duration = new LabelSlider();
duration->set_display_type(LABELSLIDER_FRAMENUMBER);
duration->set_frame_rate(sequence->frame_rate);
grid->addWidget(duration, 2, 1);
main_layout->addLayout(grid);
reverse = new QCheckBox("Reverse");
maintain_pitch = new QCheckBox("Maintain Audio Pitch");
ripple = new QCheckBox("Ripple Changes");
main_layout->addWidget(reverse);
main_layout->addWidget(maintain_pitch);
main_layout->addWidget(ripple);
QDialogButtonBox* buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
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()), this, SLOT(percent_update()));
connect(frame_rate, SIGNAL(valueChanged()), this, SLOT(frame_rate_update()));
connect(duration, SIGNAL(valueChanged()), this, SLOT(duration_update()));
}
void SpeedDialog::run() {
bool enable_frame_rate = false;
bool multiple_audio = false;
maintain_pitch->setEnabled(false);
default_frame_rate = qSNaN();
current_frame_rate = qSNaN();
current_percent = qSNaN();
default_length = -1;
current_length = -1;
for (int i=0;i<clips.size();i++) {
Clip* c = clips.at(i);
double clip_percent;
// get default frame rate/percentage
clip_percent = c->speed;
if (c->track < 0) {
bool process_video = true;
if (c->media != NULL && c->media->get_type() == MEDIA_TYPE_FOOTAGE) {
Footage* m = c->media->to_footage();
FootageStream* ms = m->get_stream_from_file_index(true, c->media_stream);
if (ms != NULL && ms->infinite_length) {
process_video = false;
}
}
if (process_video) {
double media_frame_rate = c->getMediaFrameRate();
// 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, current_frame_rate)) {
current_frame_rate = qSNaN();
}
} else {
default_frame_rate = media_frame_rate;
current_frame_rate = media_frame_rate*c->speed;
}
enable_frame_rate = true;
}
} else {
maintain_pitch->setEnabled(true);
if (!multiple_audio) {
maintain_pitch->setChecked(c->maintain_audio_pitch);
multiple_audio = true;
} else if (!maintain_pitch->isTristate() && maintain_pitch->isChecked() != c->maintain_audio_pitch) {
maintain_pitch->setCheckState(Qt::PartiallyChecked);
maintain_pitch->setTristate(true);
}
}
if (i == 0) {
reverse->setChecked(c->reverse);
} else if (c->reverse != reverse->isChecked()) {
reverse->setTristate(true);
reverse->setCheckState(Qt::PartiallyChecked);
}
// get default length
long clip_default_length = qRound(c->getLength() * clip_percent);
if (i == 0) {
current_length = c->getLength();
default_length = clip_default_length;
current_percent = clip_percent;
} else {
if (current_length != -1 && c->getLength() != 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->set_minimum_value(1);
percent->set_minimum_value(0.0001);
duration->set_minimum_value(1);
frame_rate->setEnabled(enable_frame_rate);
frame_rate->set_default_value(default_frame_rate);
frame_rate->set_value(current_frame_rate, false);
percent->set_value(current_percent, false);
duration->set_default_value(default_length);
duration->set_value((current_length == -1) ? qSNaN() : current_length, false);
exec();
}
void SpeedDialog::percent_update() {
bool got_fr = false;
double fr_val = qSNaN();
long len_val = -1;
for (int i=0;i<clips.size();i++) {
Clip* c = clips.at(i);
// get frame rate
if (frame_rate->isEnabled() && c->track < 0) {
double clip_fr = c->getMediaFrameRate() * 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->getLength() * c->speed);
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->set_value(fr_val, false);
duration->set_value((len_val == -1) ? qSNaN() : len_val, false);
}
void SpeedDialog::duration_update() {
double pc_val = qSNaN();
bool got_fr = false;
double fr_val = qSNaN();
for (int i=0;i<clips.size();i++) {
Clip* c = clips.at(i);
// get percent
long clip_default_length = qRound(c->getLength() * c->speed);
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->getMediaFrameRate() * 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->set_value(fr_val, false);
percent->set_value(pc_val, false);
}
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;i<clips.size();i++) {
Clip* c = clips.at(i);
// check if all selected clips are currently the same speed
if (i == 0) {
old_pc_val = c->speed;
} else if (!qIsNaN(old_pc_val) && !qFuzzyCompare(c->speed, 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->getMediaFrameRate();
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->getLength() * c->speed) / 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;i<clips.size();i++) {
Clip* c = clips.at(i);
if (c->track >= 0) {
long new_clip_len = (qIsNaN(old_pc_val) || qIsNaN(pc_val)) ? c->getLength() : ((c->getLength() * c->speed) / pc_val);
if (len_val > -1 && new_clip_len != len_val) {
len_val = -1;
break;
}
}
}
percent->set_value(pc_val, false);
duration->set_value((len_val == -1) ? qSNaN() : len_val, false);
}
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 / speed);
proposed_out = c->timeline_in + (c->getLength() * multiplier);
ca->append(new SetSpeedAction(c, speed));
if (!ripple && proposed_out > c->timeline_out) {
for (int i=0;i<c->sequence->clips.size();i++) {
Clip* compare = c->sequence->clips.at(i);
if (compare != NULL
&& 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);
move_clip(ca, c, c->timeline_in, proposed_out, 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;
sequence->selections.append(sel);
}
void SpeedDialog::accept() {
ComboAction* ca = new ComboAction();
SetSelectionsCommand* sel_command = new SetSelectionsCommand(sequence);
sel_command->old_data = sequence->selections;
long earliest_point = LONG_MAX;
long longest_ripple = LONG_MIN;
for (int i=0;i<clips.size();i++) {
Clip* c = clips.at(i);
if (c->open) close_clip(c);
if (c->track >= 0
&& maintain_pitch->checkState() != Qt::PartiallyChecked
&& c->maintain_audio_pitch != maintain_pitch->isChecked()) {
ca->append(new SetBool(&c->maintain_audio_pitch, maintain_pitch->isChecked()));
}
if (reverse->checkState() != Qt::PartiallyChecked && c->reverse != reverse->isChecked()) {
long new_clip_in = (c->getMaximumLength() - (c->getLength() + c->clip_in));
move_clip(ca, c, c->timeline_in, c->timeline_out, new_clip_in, c->track);
c->clip_in = new_clip_in;
ca->append(new SetBool(&c->reverse, reverse->isChecked()));
}
}
if (!qIsNaN(percent->value())) {
// simply set speed
for (int i=0;i<clips.size();i++) {
Clip* c = clips.at(i);
set_speed(ca, c, percent->value(), ripple->isChecked(), earliest_point, longest_ripple);
}
} else if (!qIsNaN(frame_rate->value())) {
bool can_change_all = true;
double cached_speed;
double cached_fr = qSNaN();
// see if we can use the frame rate to change all the speeds
for (int i=0;i<clips.size();i++) {
Clip* c = clips.at(i);
if (i == 0) {
cached_speed = c->speed;
} else if (!qFuzzyCompare(cached_speed, c->speed)) {
can_change_all = false;
}
if (c->track < 0) {
if (qIsNaN(cached_fr)) {
cached_fr = c->getMediaFrameRate();
} else if (!qFuzzyCompare(cached_fr, c->getMediaFrameRate())) {
can_change_all = false;
break;
}
}
}
// make changes
for (int i=0;i<clips.size();i++) {
Clip* c = clips.at(i);
if (c->track < 0) {
set_speed(ca, c, frame_rate->value() / c->getMediaFrameRate(), 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;i<clips.size();i++) {
Clip* c = clips.at(i);
set_speed(ca, c, (c->getLength() * c->speed) / duration->value(), ripple->isChecked(), earliest_point, longest_ripple);
}
}
if (ripple->isChecked()) {
ca->append(new RippleCommand(clips.at(0)->sequence, earliest_point, longest_ripple));
}
sel_command->new_data = sequence->selections;
ca->append(sel_command);
undo_stack.push(ca);
update_ui(true);
QDialog::accept();
}