implemented foundational reversed block code
Ensured renderers can handle clips that are reversed.
This commit is contained in:
@@ -33,6 +33,7 @@ SpeedDurationDialog::SpeedDurationDialog(const rational& timebase, const QList<C
|
||||
// show them all as having the same parameters
|
||||
bool same_speed = true;
|
||||
bool same_duration = true;
|
||||
bool all_reversed = true;
|
||||
|
||||
for (int i=1;i<clips.size();i++) {
|
||||
ClipBlock* prev_clip = clips_.at(i-1);
|
||||
@@ -50,8 +51,16 @@ SpeedDurationDialog::SpeedDurationDialog(const rational& timebase, const QList<C
|
||||
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) {
|
||||
if (!same_speed
|
||||
&& !same_duration
|
||||
&& !all_reversed) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -97,10 +106,15 @@ SpeedDurationDialog::SpeedDurationDialog(const rational& timebase, const QList<C
|
||||
// Pick up when the speed or duration slider changes so we can programmatically link them
|
||||
connect(speed_slider_, SIGNAL(ValueChanged(double)), this, SLOT(SpeedChanged()));
|
||||
connect(duration_slider_, SIGNAL(ValueChanged(int64_t)), this, SLOT(DurationChanged()));
|
||||
}
|
||||
|
||||
reverse_speed_checkbox_ = new QCheckBox(tr("Reverse Speed"));
|
||||
layout->addWidget(reverse_speed_checkbox_);
|
||||
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_);
|
||||
@@ -216,6 +230,10 @@ void SpeedDurationDialog::accept()
|
||||
// Change the speed by calculating the appropriate media out point for this clip
|
||||
new BlockSetMediaOutCommand(clip, new_media_out, command);
|
||||
}
|
||||
|
||||
if (!reverse_speed_checkbox_->isTristate() && clip->is_reversed() != reverse_speed_checkbox_->isChecked()) {
|
||||
new BlockReverseCommand(clip, command);
|
||||
}
|
||||
}
|
||||
|
||||
olive::undo_stack.pushIfHasChildren(command);
|
||||
@@ -311,3 +329,22 @@ void SpeedDurationDialog::DurationChanged()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BlockReverseCommand::BlockReverseCommand(Block *block, QUndoCommand *parent) :
|
||||
QUndoCommand(parent),
|
||||
block_(block)
|
||||
{
|
||||
}
|
||||
|
||||
void BlockReverseCommand::redo()
|
||||
{
|
||||
rational temp = block_->media_in();
|
||||
block_->set_media_in(block_->media_out());
|
||||
block_->set_media_out(temp);
|
||||
}
|
||||
|
||||
void BlockReverseCommand::undo()
|
||||
{
|
||||
// Since it's a simple swap, we can just run redo() again
|
||||
redo();
|
||||
}
|
||||
|
||||
@@ -44,4 +44,15 @@ private slots:
|
||||
void DurationChanged();
|
||||
};
|
||||
|
||||
class BlockReverseCommand : public QUndoCommand {
|
||||
public:
|
||||
BlockReverseCommand(Block* block, QUndoCommand* parent = nullptr);
|
||||
|
||||
virtual void redo() override;
|
||||
virtual void undo() override;
|
||||
|
||||
private:
|
||||
Block* block_;
|
||||
};
|
||||
|
||||
#endif // SPEEDDURATIONDIALOG_H
|
||||
|
||||
@@ -178,7 +178,17 @@ rational Block::media_length() const
|
||||
|
||||
double Block::speed() const
|
||||
{
|
||||
return media_length().toDouble() / length().toDouble();
|
||||
return qAbs(media_length().toDouble() / length().toDouble());
|
||||
}
|
||||
|
||||
bool Block::is_still() const
|
||||
{
|
||||
return (media_in() == media_out());
|
||||
}
|
||||
|
||||
bool Block::is_reversed() const
|
||||
{
|
||||
return (media_out() < media_in());
|
||||
}
|
||||
|
||||
const QString &Block::block_name() const
|
||||
|
||||
@@ -73,6 +73,8 @@ public:
|
||||
|
||||
rational media_length() const;
|
||||
double speed() const;
|
||||
bool is_still() const;
|
||||
bool is_reversed() const;
|
||||
|
||||
const QString& block_name() const;
|
||||
void set_block_name(const QString& name);
|
||||
|
||||
@@ -70,6 +70,24 @@ NodeValueTable AudioRenderWorker::RenderBlock(const TrackOutput *track, const Ti
|
||||
samples_from_this_block = speed_adjusted_samples;
|
||||
}
|
||||
|
||||
if (b->is_reversed()) {
|
||||
int sample_size = audio_params_.samples_to_bytes(1);
|
||||
int half_buffer_sz = samples_from_this_block.size() / 2;
|
||||
char* temp_buffer = new char[sample_size];
|
||||
|
||||
for (int src_index=0;src_index<half_buffer_sz;src_index+=sample_size) {
|
||||
char* src_ptr = samples_from_this_block.data() + src_index;
|
||||
char* dst_ptr = samples_from_this_block.data() + samples_from_this_block.size() - sample_size - src_index;
|
||||
|
||||
// Simple swap
|
||||
memcpy(temp_buffer, src_ptr, sample_size);
|
||||
memcpy(src_ptr, dst_ptr, sample_size);
|
||||
memcpy(dst_ptr, temp_buffer, sample_size);
|
||||
}
|
||||
|
||||
delete [] temp_buffer;
|
||||
}
|
||||
|
||||
copied_size = samples_from_this_block.size();
|
||||
|
||||
memcpy(block_range_buffer.data()+destination_offset,
|
||||
|
||||
Reference in New Issue
Block a user