waveform: shift track waveform alongside cache
This commit is contained in:
@@ -123,6 +123,42 @@ void AudioVisualWaveform::AppendSilence(const rational &time)
|
||||
memset(&data_[old_size], 0, (data_.size() - old_size) * sizeof(SamplePerChannel));
|
||||
}
|
||||
|
||||
void AudioVisualWaveform::Shift(const rational &from, const rational &to)
|
||||
{
|
||||
int from_index = time_to_samples(from);
|
||||
int to_index = time_to_samples(to);
|
||||
|
||||
if (from_index == to_index) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (from_index > to_index) {
|
||||
// Shifting backwards <-
|
||||
int copy_sz = data_.size() - from_index;
|
||||
|
||||
for (int i=0; i<copy_sz; i++) {
|
||||
data_.replace(to_index + i, data_.at(from_index + i));
|
||||
}
|
||||
|
||||
data_.resize(data_.size() - (from_index - to_index));
|
||||
} else {
|
||||
// Shifting forwards ->
|
||||
int old_sz = data_.size();
|
||||
|
||||
int distance = (to_index - from_index);
|
||||
|
||||
data_.resize(data_.size() + distance);
|
||||
|
||||
int copy_sz = old_sz - from_index;
|
||||
|
||||
for (int i=0; i<copy_sz; i++) {
|
||||
data_.replace(data_.size() - i - 1, data_.at(old_sz - i - 1));
|
||||
}
|
||||
|
||||
memset(&data_[from_index], 0, distance * sizeof(SamplePerChannel));
|
||||
}
|
||||
}
|
||||
|
||||
QVector<AudioVisualWaveform::SamplePerChannel> AudioVisualWaveform::SumSamples(const float *samples, int nb_samples, int nb_channels)
|
||||
{
|
||||
return SumSamplesInternal<float>(samples, nb_samples, nb_channels);
|
||||
|
||||
@@ -78,6 +78,7 @@ public:
|
||||
void TrimOut(const rational& time);
|
||||
void PrependSilence(const rational& time);
|
||||
void AppendSilence(const rational& time);
|
||||
void Shift(const rational& from, const rational& to);
|
||||
|
||||
// FIXME: Move to dynamic
|
||||
static const int kSumSampleRate;
|
||||
|
||||
@@ -160,9 +160,9 @@ void TimeRangeList::InsertTimeRange(const TimeRange &range)
|
||||
append(range);
|
||||
}
|
||||
|
||||
void TimeRangeList::RemoveTimeRange(const TimeRange &range)
|
||||
void TimeRangeList::RemoveTimeRange(const TimeRange &remove)
|
||||
{
|
||||
RemoveTimeRange(this, range);
|
||||
RemoveTimeRange(this, remove);
|
||||
}
|
||||
|
||||
void TimeRangeList::RemoveTimeRange(QList<TimeRange> *list, const TimeRange &remove)
|
||||
|
||||
@@ -73,7 +73,7 @@ public:
|
||||
|
||||
void InsertTimeRange(const TimeRange& range);
|
||||
|
||||
void RemoveTimeRange(const TimeRange& range);
|
||||
void RemoveTimeRange(const TimeRange& remove);
|
||||
|
||||
static void RemoveTimeRange(QList<TimeRange>* list, const TimeRange& remove);
|
||||
|
||||
|
||||
@@ -57,9 +57,6 @@ ViewerOutput::ViewerOutput()
|
||||
|
||||
// Create UUID for this node
|
||||
uuid_ = QUuid::createUuid();
|
||||
|
||||
connect(this, &ViewerOutput::LengthChanged, &video_frame_cache_, &PlaybackCache::SetLength);
|
||||
connect(this, &ViewerOutput::LengthChanged, &audio_playback_cache_, &PlaybackCache::SetLength);
|
||||
}
|
||||
|
||||
Node *ViewerOutput::copy() const
|
||||
@@ -87,10 +84,24 @@ QString ViewerOutput::Description() const
|
||||
return tr("Interface between a Viewer panel and the node system.");
|
||||
}
|
||||
|
||||
void ViewerOutput::ShiftCache(const rational &from, const rational &to)
|
||||
void ViewerOutput::ShiftVideoCache(const rational &from, const rational &to)
|
||||
{
|
||||
video_frame_cache_.Shift(from, to);
|
||||
}
|
||||
|
||||
void ViewerOutput::ShiftAudioCache(const rational &from, const rational &to)
|
||||
{
|
||||
audio_playback_cache_.Shift(from, to);
|
||||
|
||||
foreach (TrackOutput* track, track_lists_.at(Timeline::kTrackTypeAudio)->GetTracks()) {
|
||||
track->waveform().Shift(from, to);
|
||||
}
|
||||
}
|
||||
|
||||
void ViewerOutput::ShiftCache(const rational &from, const rational &to)
|
||||
{
|
||||
ShiftVideoCache(from, to);
|
||||
ShiftAudioCache(from, to);
|
||||
}
|
||||
|
||||
void ViewerOutput::InvalidateCache(const TimeRange &range, NodeInput *from, NodeInput *source)
|
||||
@@ -179,13 +190,14 @@ void ViewerOutput::VerifyLength()
|
||||
audio_length = t.Get(NodeParam::kNumber, "length").value<rational>();
|
||||
}
|
||||
|
||||
rational timeline_length;
|
||||
video_length = qMax(video_length, track_lists_.at(Timeline::kTrackTypeVideo)->GetTotalLength());
|
||||
audio_length = qMax(audio_length, track_lists_.at(Timeline::kTrackTypeAudio)->GetTotalLength());
|
||||
rational subtitle_length = track_lists_.at(Timeline::kTrackTypeSubtitle)->GetTotalLength();
|
||||
|
||||
foreach (TrackList* tl, track_lists_) {
|
||||
timeline_length = qMax(timeline_length, tl->GetTotalLength());
|
||||
}
|
||||
video_frame_cache_.SetLength(video_length);
|
||||
audio_playback_cache_.SetLength(audio_length);
|
||||
|
||||
rational real_length = qMax(timeline_length, qMax(video_length, audio_length));
|
||||
rational real_length = qMax(subtitle_length, qMax(video_length, audio_length));
|
||||
|
||||
if (real_length != last_length_) {
|
||||
last_length_ = real_length;
|
||||
|
||||
@@ -54,6 +54,8 @@ public:
|
||||
virtual QList<CategoryID> Category() const override;
|
||||
virtual QString Description() const override;
|
||||
|
||||
void ShiftVideoCache(const rational& from, const rational& to);
|
||||
void ShiftAudioCache(const rational& from, const rational& to);
|
||||
void ShiftCache(const rational& from, const rational& to);
|
||||
|
||||
NodeInput* texture_input() const {
|
||||
|
||||
@@ -131,10 +131,19 @@ void AudioPlaybackCache::WriteSilence(const TimeRange &range)
|
||||
|
||||
void AudioPlaybackCache::ShiftEvent(const rational &from, const rational &to)
|
||||
{
|
||||
qint64 from_offset = params_.time_to_bytes(from);
|
||||
qint64 to_offset = params_.time_to_bytes(to);
|
||||
|
||||
if (from_offset == to_offset) {
|
||||
return;
|
||||
}
|
||||
|
||||
QFile f(filename_);
|
||||
if (f.open(QFile::ReadWrite)) {
|
||||
qint64 from_offset = params_.time_to_bytes(from);
|
||||
qint64 to_offset = params_.time_to_bytes(to);
|
||||
if (!f.size()) {
|
||||
return;
|
||||
}
|
||||
|
||||
qint64 chunk = qAbs(to_offset - from_offset);
|
||||
|
||||
QByteArray buf(chunk, Qt::Uninitialized);
|
||||
@@ -150,7 +159,7 @@ void AudioPlaybackCache::ShiftEvent(const rational &from, const rational &to)
|
||||
|
||||
qint64 write_offset = f.size();
|
||||
|
||||
do {
|
||||
while (read_offset != from_offset) {
|
||||
|
||||
// Calculate how much will be read this time
|
||||
qint64 chunk_sz = qMin(chunk, read_offset - from_offset);
|
||||
@@ -165,7 +174,7 @@ void AudioPlaybackCache::ShiftEvent(const rational &from, const rational &to)
|
||||
f.seek(write_offset);
|
||||
f.write(buf.data(), chunk_sz);
|
||||
|
||||
} while (read_offset != from_offset);
|
||||
}
|
||||
|
||||
// Replace remainder with silence
|
||||
f.seek(from_offset);
|
||||
@@ -175,7 +184,7 @@ void AudioPlaybackCache::ShiftEvent(const rational &from, const rational &to)
|
||||
} else {
|
||||
// Shifting backwards, we will shift bytes and truncate
|
||||
|
||||
do {
|
||||
while (from_offset != f.size()) {
|
||||
// Read region to be shifted
|
||||
f.seek(from_offset);
|
||||
qint64 read_sz = f.read(buf.data(), buf.size());
|
||||
@@ -184,7 +193,7 @@ void AudioPlaybackCache::ShiftEvent(const rational &from, const rational &to)
|
||||
// Write it at the destination
|
||||
f.seek(to_offset);
|
||||
to_offset += f.write(buf, read_sz);
|
||||
} while (from_offset != f.size());
|
||||
}
|
||||
|
||||
// Truncate
|
||||
f.resize(f.size() - chunk);
|
||||
|
||||
@@ -353,6 +353,10 @@ void RenderBackend::WorkerFinished()
|
||||
|
||||
void RenderBackend::WorkerGeneratedWaveform(const TrackOutput *copied_track, const AudioVisualWaveform& samples, const rational &r)
|
||||
{
|
||||
if (!viewer_node_) {
|
||||
return;
|
||||
}
|
||||
|
||||
TrackOutput* track = nullptr;
|
||||
|
||||
/*
|
||||
@@ -372,7 +376,6 @@ void RenderBackend::WorkerGeneratedWaveform(const TrackOutput *copied_track, con
|
||||
if (track) {
|
||||
track->waveform().set_channel_count(audio_params_.channel_count());
|
||||
track->waveform().OverwriteSums(samples, r);
|
||||
qDebug() << "Preview changed";
|
||||
emit track->PreviewChanged();
|
||||
} else {
|
||||
qDebug() << "Failed to find track";
|
||||
|
||||
@@ -57,6 +57,10 @@ void PlaybackCache::SetLength(const rational &r)
|
||||
|
||||
void PlaybackCache::Shift(const rational &from, const rational &to)
|
||||
{
|
||||
if (from == to) {
|
||||
return;
|
||||
}
|
||||
|
||||
QMutexLocker locker(&lock_);
|
||||
|
||||
// An region between `from` and `to` will be inserted or spliced out
|
||||
@@ -75,6 +79,8 @@ void PlaybackCache::Shift(const rational &from, const rational &to)
|
||||
|
||||
ShiftEvent(from, to);
|
||||
|
||||
length_ += diff;
|
||||
|
||||
if (diff > rational()) {
|
||||
// If shifting forward, add this section to the invalidated region
|
||||
TimeRange invalidate_range(from, to);
|
||||
@@ -113,12 +119,15 @@ void PlaybackCache::NoLockSetLength(const rational &r)
|
||||
return;
|
||||
}
|
||||
|
||||
TimeRange range_diff(length_, r);
|
||||
|
||||
if (r > length_) {
|
||||
// If new length is greater, simply extend the invalidated range for now
|
||||
invalidated_.InsertTimeRange(TimeRange(length_, r));
|
||||
invalidated_.InsertTimeRange(range_diff);
|
||||
} else {
|
||||
// If new length is smaller, removed hashes
|
||||
invalidated_.RemoveTimeRange(TimeRange(r, length_));
|
||||
invalidated_.RemoveTimeRange(range_diff);
|
||||
RemoveRangeFromJobs(range_diff);
|
||||
}
|
||||
|
||||
LengthChangedEvent(length_, r);
|
||||
|
||||
@@ -1226,6 +1226,12 @@ void TrackListRippleRemoveAreaCommand::redo_internal()
|
||||
if (all_tracks_unlocked_) {
|
||||
// We can optimize here by simply shifting the whole cache forward instead of re-caching
|
||||
// everything following this time
|
||||
if (list_->type() == Timeline::kTrackTypeVideo) {
|
||||
static_cast<ViewerOutput*>(list_->parent())->ShiftVideoCache(out_, in_);
|
||||
} else if (list_->type() == Timeline::kTrackTypeAudio) {
|
||||
static_cast<ViewerOutput*>(list_->parent())->ShiftAudioCache(out_, in_);
|
||||
}
|
||||
|
||||
foreach (TrackOutput* track, working_tracks_) {
|
||||
track->BlockInvalidateCache();
|
||||
}
|
||||
@@ -1236,13 +1242,6 @@ void TrackListRippleRemoveAreaCommand::redo_internal()
|
||||
}
|
||||
|
||||
if (all_tracks_unlocked_) {
|
||||
// Shift cache
|
||||
if (list_->type() == Timeline::kTrackTypeVideo) {
|
||||
static_cast<ViewerOutput*>(list_->parent())->video_frame_cache()->Shift(out_, in_);
|
||||
} else if (list_->type() == Timeline::kTrackTypeAudio) {
|
||||
static_cast<ViewerOutput*>(list_->parent())->audio_playback_cache()->Shift(out_, in_);
|
||||
}
|
||||
|
||||
foreach (TrackOutput* track, working_tracks_) {
|
||||
track->UnblockInvalidateCache();
|
||||
track->PushLengthChangeSignal();
|
||||
@@ -1255,6 +1254,12 @@ void TrackListRippleRemoveAreaCommand::undo_internal()
|
||||
if (all_tracks_unlocked_) {
|
||||
// We can optimize here by simply shifting the whole cache forward instead of re-caching
|
||||
// everything following this time
|
||||
if (list_->type() == Timeline::kTrackTypeVideo) {
|
||||
static_cast<ViewerOutput*>(list_->parent())->ShiftVideoCache(in_, out_);
|
||||
} else if (list_->type() == Timeline::kTrackTypeAudio) {
|
||||
static_cast<ViewerOutput*>(list_->parent())->ShiftAudioCache(in_, out_);
|
||||
}
|
||||
|
||||
foreach (TrackOutput* track, working_tracks_) {
|
||||
track->BlockInvalidateCache();
|
||||
}
|
||||
@@ -1265,17 +1270,10 @@ void TrackListRippleRemoveAreaCommand::undo_internal()
|
||||
}
|
||||
|
||||
if (all_tracks_unlocked_) {
|
||||
// Shift cache back
|
||||
foreach (TrackOutput* track, working_tracks_) {
|
||||
track->UnblockInvalidateCache();
|
||||
track->PushLengthChangeSignal();
|
||||
}
|
||||
|
||||
if (list_->type() == Timeline::kTrackTypeVideo) {
|
||||
static_cast<ViewerOutput*>(list_->parent())->video_frame_cache()->Shift(in_, out_);
|
||||
} else if (list_->type() == Timeline::kTrackTypeAudio) {
|
||||
static_cast<ViewerOutput*>(list_->parent())->audio_playback_cache()->Shift(in_, out_);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1387,9 +1385,9 @@ void TrackListRippleToolCommand::redo_internal()
|
||||
}
|
||||
|
||||
if (track_list_->type() == Timeline::kTrackTypeVideo) {
|
||||
static_cast<ViewerOutput*>(track_list_->parent())->video_frame_cache()->Shift(old_latest_pt, new_latest_pt);
|
||||
static_cast<ViewerOutput*>(track_list_->parent())->ShiftVideoCache(old_latest_pt, new_latest_pt);
|
||||
} else if (track_list_->type() == Timeline::kTrackTypeAudio) {
|
||||
static_cast<ViewerOutput*>(track_list_->parent())->audio_playback_cache()->Shift(old_latest_pt, new_latest_pt);
|
||||
static_cast<ViewerOutput*>(track_list_->parent())->ShiftAudioCache(old_latest_pt, new_latest_pt);
|
||||
}
|
||||
|
||||
foreach (const RippleInfo& info, info_) {
|
||||
@@ -1400,6 +1398,8 @@ void TrackListRippleToolCommand::redo_internal()
|
||||
info.track->InvalidateCache(TimeRange(earliest_pt, new_latest_pt),
|
||||
info.track->block_input(),
|
||||
info.track->block_input());
|
||||
} else {
|
||||
info.track->PushLengthChangeSignal();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user