diff --git a/app/audio/audiovisualwaveform.cpp b/app/audio/audiovisualwaveform.cpp index a58a1cd11..d6f718619 100644 --- a/app/audio/audiovisualwaveform.cpp +++ b/app/audio/audiovisualwaveform.cpp @@ -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 + 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 AudioVisualWaveform::SumSamples(const float *samples, int nb_samples, int nb_channels) { return SumSamplesInternal(samples, nb_samples, nb_channels); diff --git a/app/audio/audiovisualwaveform.h b/app/audio/audiovisualwaveform.h index 434461860..9a262c18b 100644 --- a/app/audio/audiovisualwaveform.h +++ b/app/audio/audiovisualwaveform.h @@ -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; diff --git a/app/common/timerange.cpp b/app/common/timerange.cpp index 9e91e721d..d5202a5e8 100644 --- a/app/common/timerange.cpp +++ b/app/common/timerange.cpp @@ -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 *list, const TimeRange &remove) diff --git a/app/common/timerange.h b/app/common/timerange.h index ed69548af..42fdea34a 100644 --- a/app/common/timerange.h +++ b/app/common/timerange.h @@ -73,7 +73,7 @@ public: void InsertTimeRange(const TimeRange& range); - void RemoveTimeRange(const TimeRange& range); + void RemoveTimeRange(const TimeRange& remove); static void RemoveTimeRange(QList* list, const TimeRange& remove); diff --git a/app/node/output/viewer/viewer.cpp b/app/node/output/viewer/viewer.cpp index 0ef6ac163..d5ca30249 100644 --- a/app/node/output/viewer/viewer.cpp +++ b/app/node/output/viewer/viewer.cpp @@ -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 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; diff --git a/app/node/output/viewer/viewer.h b/app/node/output/viewer/viewer.h index aab99fda6..0be3bb851 100644 --- a/app/node/output/viewer/viewer.h +++ b/app/node/output/viewer/viewer.h @@ -54,6 +54,8 @@ public: virtual QList 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 { diff --git a/app/render/audioplaybackcache.cpp b/app/render/audioplaybackcache.cpp index fd6d89b0f..a825a2b74 100644 --- a/app/render/audioplaybackcache.cpp +++ b/app/render/audioplaybackcache.cpp @@ -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); diff --git a/app/render/backend/renderbackend.cpp b/app/render/backend/renderbackend.cpp index 6a214fc5b..3cd726bf9 100644 --- a/app/render/backend/renderbackend.cpp +++ b/app/render/backend/renderbackend.cpp @@ -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"; diff --git a/app/render/playbackcache.cpp b/app/render/playbackcache.cpp index b223db9b0..dd83d8825 100644 --- a/app/render/playbackcache.cpp +++ b/app/render/playbackcache.cpp @@ -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); diff --git a/app/widget/timelinewidget/undo/undo.cpp b/app/widget/timelinewidget/undo/undo.cpp index d447bca77..9ad33d36e 100644 --- a/app/widget/timelinewidget/undo/undo.cpp +++ b/app/widget/timelinewidget/undo/undo.cpp @@ -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(list_->parent())->ShiftVideoCache(out_, in_); + } else if (list_->type() == Timeline::kTrackTypeAudio) { + static_cast(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(list_->parent())->video_frame_cache()->Shift(out_, in_); - } else if (list_->type() == Timeline::kTrackTypeAudio) { - static_cast(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(list_->parent())->ShiftVideoCache(in_, out_); + } else if (list_->type() == Timeline::kTrackTypeAudio) { + static_cast(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(list_->parent())->video_frame_cache()->Shift(in_, out_); - } else if (list_->type() == Timeline::kTrackTypeAudio) { - static_cast(list_->parent())->audio_playback_cache()->Shift(in_, out_); - } } } @@ -1387,9 +1385,9 @@ void TrackListRippleToolCommand::redo_internal() } if (track_list_->type() == Timeline::kTrackTypeVideo) { - static_cast(track_list_->parent())->video_frame_cache()->Shift(old_latest_pt, new_latest_pt); + static_cast(track_list_->parent())->ShiftVideoCache(old_latest_pt, new_latest_pt); } else if (track_list_->type() == Timeline::kTrackTypeAudio) { - static_cast(track_list_->parent())->audio_playback_cache()->Shift(old_latest_pt, new_latest_pt); + static_cast(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(); } } }