From 2f7dc3c98b6c571a5c0cfcf3ee44c5d82d4d122d Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Sun, 29 May 2022 17:52:35 -0700 Subject: [PATCH] implemented separate waveform cache --- app/audio/audiovisualwaveform.cpp | 68 ++++++----- app/audio/audiovisualwaveform.h | 7 +- app/node/block/clip/clip.cpp | 20 +++- app/node/block/clip/clip.h | 6 +- app/node/node.cpp | 1 + app/node/node.h | 7 ++ app/node/output/viewer/viewer.cpp | 35 +++++- app/node/output/viewer/viewer.h | 16 +++ app/node/project/footage/footage.cpp | 2 + .../project/serializer/serializer220403.cpp | 3 + app/render/CMakeLists.txt | 2 + app/render/audioplaybackcache.cpp | 20 ---- app/render/audioplaybackcache.h | 12 -- app/render/audiowaveformcache.cpp | 101 +++++++++++++++++ app/render/audiowaveformcache.h | 85 ++++++++++++++ app/render/previewautocacher.cpp | 106 +++++------------- app/render/previewautocacher.h | 9 +- app/render/rendermanager.cpp | 1 + app/render/rendermanager.h | 2 + app/render/renderprocessor.cpp | 4 +- app/widget/audiomonitor/audiomonitor.cpp | 7 +- app/widget/audiomonitor/audiomonitor.h | 9 +- app/widget/timelinewidget/tool/pointer.cpp | 3 +- .../timelinewidget/undo/timelineundosplit.cpp | 5 +- .../timelinewidget/view/timelineview.cpp | 4 +- app/widget/viewer/audiowaveformview.cpp | 16 ++- app/widget/viewer/audiowaveformview.h | 4 +- app/widget/viewer/viewer.cpp | 4 +- 28 files changed, 381 insertions(+), 178 deletions(-) create mode 100644 app/render/audiowaveformcache.cpp create mode 100644 app/render/audiowaveformcache.h diff --git a/app/audio/audiovisualwaveform.cpp b/app/audio/audiovisualwaveform.cpp index 0fb9c215a..c9b2ef50e 100644 --- a/app/audio/audiovisualwaveform.cpp +++ b/app/audio/audiovisualwaveform.cpp @@ -193,38 +193,12 @@ void AudioVisualWaveform::OverwriteSilence(const rational &start, const rational } } -void AudioVisualWaveform::Shift(const rational &from, const rational &to) -{ - for (auto it=mipmapped_data_.begin(); it!=mipmapped_data_.end(); it++) { - rational rate = it->first; - double rate_dbl = rate.toDouble(); - Sample& data = it->second; - - int from_index = time_to_samples(from, rate_dbl); - int to_index = time_to_samples(to, rate_dbl); - - if (from_index == to_index) { - continue; - } - - if (from_index >= data.size()) { - continue; - } - - if (from_index > to_index) { - // Shifting backwards <- - data.remove(to_index, from_index - to_index); - } else { - // Shifting forwards -> - data.insert(from_index, to_index - from_index, {0, 0}); - } - } - - length_ = qMax(rational(0), length_ + (to-from)); -} - void AudioVisualWaveform::TrimIn(const rational &length) { + if (length == 0) { + return; + } + for (auto it=mipmapped_data_.begin(); it!=mipmapped_data_.end(); it++) { rational rate = it->first; double rate_dbl = rate.toDouble(); @@ -255,6 +229,40 @@ AudioVisualWaveform AudioVisualWaveform::Mid(const rational &offset) const return mid; } +AudioVisualWaveform AudioVisualWaveform::Mid(const rational &offset, const rational &length) const +{ + AudioVisualWaveform mid = *this; + + mid.TrimRange(offset, length); + + return mid; +} + +void AudioVisualWaveform::Resize(const rational &length) +{ + if (length_ == length) { + return; + } + + for (auto it=mipmapped_data_.begin(); it!=mipmapped_data_.end(); it++) { + rational rate = it->first; + double rate_dbl = rate.toDouble(); + Sample& data = it->second; + + int chop_length = time_to_samples(length, rate_dbl); + + data.resize(chop_length); + } + + length_ = length; +} + +void AudioVisualWaveform::TrimRange(const rational &in, const rational &length) +{ + TrimIn(in); + Resize(length); +} + AudioVisualWaveform::Sample AudioVisualWaveform::GetSummaryFromTime(const rational &start, const rational &length) const { // Find mipmap that requires diff --git a/app/audio/audiovisualwaveform.h b/app/audio/audiovisualwaveform.h index 389f72980..e2202b469 100644 --- a/app/audio/audiovisualwaveform.h +++ b/app/audio/audiovisualwaveform.h @@ -90,11 +90,14 @@ public: void OverwriteSilence(const rational &start, const rational &length); - void Shift(const rational& from, const rational& to); - void TrimIn(const rational &length); AudioVisualWaveform Mid(const rational &offset) const; + AudioVisualWaveform Mid(const rational &offset, const rational &length) const; + + void Resize(const rational &length); + + void TrimRange(const rational &in, const rational &length); Sample GetSummaryFromTime(const rational& start, const rational& length) const; diff --git a/app/node/block/clip/clip.cpp b/app/node/block/clip/clip.cpp index e0aaa25bf..6a61b73ac 100644 --- a/app/node/block/clip/clip.cpp +++ b/app/node/block/clip/clip.cpp @@ -38,7 +38,8 @@ const QString ClipBlock::kMaintainAudioPitchInput = QStringLiteral("maintain_aud ClipBlock::ClipBlock() : in_transition_(nullptr), out_transition_(nullptr), - connected_viewer_(nullptr) + connected_viewer_(nullptr), + autocache_(false) { AddInput(kMediaInInput, NodeValue::kRational, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable)); SetInputProperty(kMediaInInput, QStringLiteral("view"), RationalSlider::kTime); @@ -188,8 +189,14 @@ void ClipBlock::InvalidateCache(const TimeRange& range, const QString& from, int TimeRange max_range = InputTimeAdjustment(from, element, TimeRange(0, length())); if (type == Track::kVideo) { emit connected->thumbnail_cache()->Request(range.Intersected(max_range), PlaybackCache::kPreviewsOnly); + if (autocache_) { + emit connected->video_frame_cache()->Request(range.Intersected(max_range), PlaybackCache::kPreviewsOnly); + } } else if (type == Track::kAudio) { - emit connected->audio_playback_cache()->Request(range.Intersected(max_range), PlaybackCache::kPreviewsOnly); + emit connected->waveform_cache()->Request(range.Intersected(max_range), PlaybackCache::kPreviewsOnly); + if (autocache_) { + emit connected->audio_playback_cache()->Request(range.Intersected(max_range), PlaybackCache::kPreviewsOnly); + } } } } @@ -251,7 +258,7 @@ void ClipBlock::InputConnectedEvent(const QString &input, int element, Node *out if (input == kBufferIn) { connect(output->thumbnail_cache(), &FrameHashCache::Validated, this, &Block::PreviewChanged); - connect(output->audio_playback_cache(), &AudioPlaybackCache::WaveformUpdated, this, &Block::PreviewChanged); + connect(output->waveform_cache(), &AudioPlaybackCache::Validated, this, &Block::PreviewChanged); } } @@ -261,7 +268,7 @@ void ClipBlock::InputDisconnectedEvent(const QString &input, int element, Node * if (input == kBufferIn) { disconnect(output->thumbnail_cache(), &FrameHashCache::Validated, this, &Block::PreviewChanged); - disconnect(output->audio_playback_cache(), &AudioPlaybackCache::WaveformUpdated, this, &Block::PreviewChanged); + disconnect(output->waveform_cache(), &AudioPlaybackCache::Validated, this, &Block::PreviewChanged); } } @@ -324,7 +331,10 @@ void ClipBlock::ConnectedToPreviewEvent() emit connected->thumbnail_cache()->Request(r, PlaybackCache::kPreviewsOnly); } } else if (type == Track::kAudio) { - //emit connected->audio_playback_cache()->Request(range.Intersected(max_range), PlaybackCache::kPreviewsOnly); + TimeRangeList invalid = connected->waveform_cache()->GetInvalidatedRanges(max_range); + for (const TimeRange &r : invalid) { + emit connected->waveform_cache()->Request(r, PlaybackCache::kPreviewsOnly); + } } } } diff --git a/app/node/block/clip/clip.h b/app/node/block/clip/clip.h index feff9c4d2..b43bfde09 100644 --- a/app/node/block/clip/clip.h +++ b/app/node/block/clip/clip.h @@ -128,10 +128,10 @@ public: } } - const AudioVisualWaveform *waveform() + const AudioWaveformCache *waveform() { if (Node *n = GetConnectedOutput(kBufferIn)) { - return &n->audio_playback_cache()->visual(); + return n->waveform_cache(); } else { return nullptr; } @@ -185,6 +185,8 @@ private: ViewerOutput *connected_viewer_; + bool autocache_; + private: rational last_media_in_; diff --git a/app/node/node.cpp b/app/node/node.cpp index 8f18fb996..7d3bc7345 100644 --- a/app/node/node.cpp +++ b/app/node/node.cpp @@ -56,6 +56,7 @@ Node::Node() : video_cache_ = new FrameHashCache(this); thumbnail_cache_ = new FrameHashCache(this); audio_cache_ = new AudioPlaybackCache(this); + waveform_cache_ = new AudioWaveformCache(this); } Node::~Node() diff --git a/app/node/node.h b/app/node/node.h index 13beb3d1c..6a96f97a1 100644 --- a/app/node/node.h +++ b/app/node/node.h @@ -40,6 +40,7 @@ #include "node/param.h" #include "render/audioparams.h" #include "render/audioplaybackcache.h" +#include "render/audiowaveformcache.h" #include "render/framehashcache.h" #include "render/job/generatejob.h" #include "render/job/samplejob.h" @@ -236,6 +237,11 @@ public: return audio_cache_; } + AudioWaveformCache* waveform_cache() const + { + return waveform_cache_; + } + virtual TimeRange GetVideoCacheRange() const { return TimeRange(); } virtual TimeRange GetAudioCacheRange() const { return TimeRange(); } @@ -1413,6 +1419,7 @@ private: FrameHashCache *thumbnail_cache_; AudioPlaybackCache *audio_cache_; + AudioWaveformCache *waveform_cache_; private slots: /** diff --git a/app/node/output/viewer/viewer.cpp b/app/node/output/viewer/viewer.cpp index 9330e414d..84492de8f 100644 --- a/app/node/output/viewer/viewer.cpp +++ b/app/node/output/viewer/viewer.cpp @@ -37,7 +37,9 @@ const QString ViewerOutput::kSamplesInput = QStringLiteral("samples_in"); ViewerOutput::ViewerOutput(bool create_buffer_inputs, bool create_default_streams) : last_length_(0), video_length_(0), - audio_length_(0) + audio_length_(0), + autocache_input_video_(false), + autocache_input_audio_(false) { AddInput(kVideoParamsInput, NodeValue::kVideoParams, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable | kInputFlagArray | kInputFlagHidden)); @@ -219,6 +221,22 @@ void ViewerOutput::InvalidateCache(const TimeRange& range, const QString& from, { Q_UNUSED(element) + if (Node *connected = GetConnectedOutput(from, element)) { + if (from == kTextureInput) { + //emit connected->thumbnail_cache()->Request(range.Intersected(max_range), PlaybackCache::kPreviewsOnly); + if (autocache_input_video_) { + TimeRange max_range = InputTimeAdjustment(from, element, TimeRange(0, GetVideoLength())); + emit connected->video_frame_cache()->Request(range.Intersected(max_range), PlaybackCache::kPreviewsOnly); + } + } else if (from == kSamplesInput) { + TimeRange max_range = InputTimeAdjustment(from, element, TimeRange(0, GetAudioLength())); + emit connected->waveform_cache()->Request(range.Intersected(max_range), PlaybackCache::kPreviewsOnly); + if (autocache_input_audio_) { + emit connected->audio_playback_cache()->Request(range.Intersected(max_range), PlaybackCache::kPreviewsOnly); + } + } + } + VerifyLength(); super::InvalidateCache(range, from, element, options); @@ -298,6 +316,8 @@ void ViewerOutput::InputConnectedEvent(const QString &input, int element, Node * { if (input == kTextureInput) { emit TextureInputChanged(); + } else if (input == kSamplesInput) { + connect(output->waveform_cache(), &AudioWaveformCache::Validated, this, &ViewerOutput::ConnectedWaveformChanged); } super::InputConnectedEvent(input, element, output); @@ -307,6 +327,8 @@ void ViewerOutput::InputDisconnectedEvent(const QString &input, int element, Nod { if (input == kTextureInput) { emit TextureInputChanged(); + } else if (input == kSamplesInput) { + disconnect(output->waveform_cache(), &AudioWaveformCache::Validated, this, &ViewerOutput::ConnectedWaveformChanged); } super::InputDisconnectedEvent(input, element, output); @@ -364,6 +386,17 @@ Node::ValueHint ViewerOutput::GetConnectedSampleValueHint() return GetValueHintForInput(kSamplesInput); } +void ViewerOutput::ConnectedToPreviewEvent() +{ + if (Node *connected = GetConnectedOutput(kSamplesInput)) { + TimeRange max_range = InputTimeAdjustment(kSamplesInput, -1, TimeRange(0, GetAudioLength())); + TimeRangeList invalid = connected->waveform_cache()->GetInvalidatedRanges(max_range); + for (const TimeRange &r : invalid) { + emit connected->waveform_cache()->Request(r, PlaybackCache::kPreviewsOnly); + } + } +} + void ViewerOutput::InputValueChangedEvent(const QString &input, int element) { if (element == 0) { diff --git a/app/node/output/viewer/viewer.h b/app/node/output/viewer/viewer.h index dff2e8e7c..daca1d4a4 100644 --- a/app/node/output/viewer/viewer.h +++ b/app/node/output/viewer/viewer.h @@ -130,6 +130,15 @@ public: return GetVideoStreamCount() + GetAudioStreamCount() + GetSubtitleStreamCount(); } + const AudioWaveformCache *GetConnectedWaveform() + { + if (Node *n = GetConnectedSampleOutput()) { + return n->waveform_cache(); + } else { + return nullptr; + } + } + bool HasEnabledVideoStreams() const; bool HasEnabledAudioStreams() const; bool HasEnabledSubtitleStreams() const; @@ -173,6 +182,8 @@ public: virtual ValueHint GetConnectedSampleValueHint(); + virtual void ConnectedToPreviewEvent() override; + static const QString kVideoParamsInput; static const QString kAudioParamsInput; static const QString kSubtitleParamsInput; @@ -198,6 +209,8 @@ signals: void SampleRateChanged(int sr); + void ConnectedWaveformChanged(); + public slots: void VerifyLength(); @@ -224,6 +237,9 @@ private: TimelinePoints *timeline_points_; + bool autocache_input_video_; + bool autocache_input_audio_; + }; } diff --git a/app/node/project/footage/footage.cpp b/app/node/project/footage/footage.cpp index 4a5e5f0a5..d38b610c0 100644 --- a/app/node/project/footage/footage.cpp +++ b/app/node/project/footage/footage.cpp @@ -63,6 +63,8 @@ Footage::Footage(const QString &filename) : check_timer->setInterval(5000); connect(check_timer, &QTimer::timeout, this, &Footage::CheckFootage); check_timer->start(); + + connect(this->waveform_cache(), &AudioWaveformCache::Validated, this, &ViewerOutput::ConnectedWaveformChanged); } void Footage::Retranslate() diff --git a/app/node/project/serializer/serializer220403.cpp b/app/node/project/serializer/serializer220403.cpp index 00e5dc25b..a78e11b5b 100644 --- a/app/node/project/serializer/serializer220403.cpp +++ b/app/node/project/serializer/serializer220403.cpp @@ -552,6 +552,8 @@ void ProjectSerializer220403::LoadNode(Node *node, XMLNodeData &xml_node_data, Q node->video_frame_cache()->SetUuid(reader->readElementText()); } else if (reader->name() == QStringLiteral("thumb")) { node->thumbnail_cache()->SetUuid(reader->readElementText()); + } else if (reader->name() == QStringLiteral("waveform")) { + node->waveform_cache()->SetUuid(reader->readElementText()); } else { reader->skipCurrentElement(); } @@ -616,6 +618,7 @@ void ProjectSerializer220403::SaveNode(Node *node, QXmlStreamWriter *writer) con writer->writeTextElement(QStringLiteral("audio"), node->audio_playback_cache()->GetUuid().toString()); writer->writeTextElement(QStringLiteral("video"), node->video_frame_cache()->GetUuid().toString()); writer->writeTextElement(QStringLiteral("thumb"), node->thumbnail_cache()->GetUuid().toString()); + writer->writeTextElement(QStringLiteral("waveform"), node->waveform_cache()->GetUuid().toString()); writer->writeEndElement(); // caches diff --git a/app/render/CMakeLists.txt b/app/render/CMakeLists.txt index 8a37e1776..b19e98d0c 100644 --- a/app/render/CMakeLists.txt +++ b/app/render/CMakeLists.txt @@ -24,6 +24,8 @@ set(OLIVE_SOURCES render/audioparams.h render/audioplaybackcache.cpp render/audioplaybackcache.h + render/audiowaveformcache.cpp + render/audiowaveformcache.h render/color.cpp render/color.h render/colorprocessor.cpp diff --git a/app/render/audioplaybackcache.cpp b/app/render/audioplaybackcache.cpp index 55dba2268..6113a6fd9 100644 --- a/app/render/audioplaybackcache.cpp +++ b/app/render/audioplaybackcache.cpp @@ -48,9 +48,6 @@ void AudioPlaybackCache::SetParameters(const AudioParams ¶ms) } params_ = params; - visual_.set_channel_count(params_.channel_count()); - - emit ParametersChanged(); } void AudioPlaybackCache::WritePCM(const TimeRange &range, const TimeRangeList &valid_ranges, const SampleBuffer &samples) @@ -62,23 +59,6 @@ void AudioPlaybackCache::WritePCM(const TimeRange &range, const TimeRangeList &v } } -void AudioPlaybackCache::WriteWaveform(const TimeRange &range, const TimeRangeList &valid_ranges, const AudioVisualWaveform *waveform) -{ - // Write each valid range to the segments - foreach (const TimeRange& r, valid_ranges) { - // Write visual - if (waveform) { - visual_.OverwriteSums(*waveform, r.in(), r.in() - range.in(), r.length()); - } else { - visual_.OverwriteSilence(r.in(), r.length()); - } - } - - if (!valid_ranges.isEmpty()) { - emit WaveformUpdated(); - } -} - void AudioPlaybackCache::WriteSilence(const TimeRange &range) { // WritePCM will automatically fill non-existent bytes with silence, so we just have to send diff --git a/app/render/audioplaybackcache.h b/app/render/audioplaybackcache.h index 94f556037..6a7949482 100644 --- a/app/render/audioplaybackcache.h +++ b/app/render/audioplaybackcache.h @@ -68,18 +68,8 @@ public: void WritePCM(const TimeRange &range, const TimeRangeList &valid_ranges, const SampleBuffer &samples); - void WriteWaveform(const TimeRange &range, const TimeRangeList &valid_ranges, const AudioVisualWaveform *waveform); - void WriteSilence(const TimeRange &range); - const AudioVisualWaveform &visual() const { return visual_; } - void set_visual(const AudioVisualWaveform &v) { visual_ = v; } - -signals: - void ParametersChanged(); - - void WaveformUpdated(); - private: bool WritePartOfSampleBuffer(const SampleBuffer &samples, const rational &write_start, const rational &buffer_start, const rational &length); @@ -89,8 +79,6 @@ private: AudioParams params_; - AudioVisualWaveform visual_; - }; } diff --git a/app/render/audiowaveformcache.cpp b/app/render/audiowaveformcache.cpp new file mode 100644 index 000000000..ba3a0f506 --- /dev/null +++ b/app/render/audiowaveformcache.cpp @@ -0,0 +1,101 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2022 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 "audiowaveformcache.h" + +namespace olive { + +AudioWaveformCache::AudioWaveformCache(QObject *parent) : + PlaybackCache{parent} +{ +} + +void AudioWaveformCache::WriteWaveform(const TimeRange &range, const TimeRangeList &valid_ranges, const AudioVisualWaveform *waveform) +{ + // Write each valid range to the segments + foreach (const TimeRange& r, valid_ranges) { + // Write visual + TimeRangeList::util_remove(&waveforms_, r); + + if (waveform) { + TimeRangeWithWaveform wv = r; + rational local_start = r.in() - range.in(); + wv.waveform = waveform->Mid(local_start, r.length()); + waveforms_.append(wv); + } + + Validate(r); + } +} + +void AudioWaveformCache::Draw(QPainter *painter, const QRect &rect, const double &scale, const rational &start_time) const +{ + rational end = start_time + rational::fromDouble(rect.width() / scale); + TimeRange draw_range(start_time, end); + + foreach (const TimeRangeWithWaveform &wv, waveforms_) { + if (wv.OverlapsWith(draw_range)) { + rational substart = std::max(wv.in(), draw_range.in()); + rational subend = std::min(wv.out(), draw_range.out()); + + QRect subrect = rect; + subrect.setLeft(subrect.left() + (substart - draw_range.in()).toDouble()*scale); + subrect.setWidth((subend - substart).toDouble()*scale); + + rational local_start = substart - wv.in(); + AudioVisualWaveform::DrawWaveform(painter, subrect, scale, wv.waveform, local_start); + } + } +} + +AudioVisualWaveform::Sample AudioWaveformCache::GetSummaryFromTime(const rational &start, const rational &length) const +{ + QMap sample; + + TimeRange acquire(start, start+length); + foreach (const TimeRangeWithWaveform &wv, waveforms_) { + if (wv.OverlapsWith(acquire)) { + TimeRange this_range = wv.Intersected(acquire); + auto sum = wv.waveform.GetSummaryFromTime(this_range.in() - wv.in(), this_range.length()); + sample.insert(this_range.in(), sum); + } + } + + AudioVisualWaveform::Sample result; + + for (auto it=sample.cbegin(); it!=sample.cend(); it++) { + result.append(it.value()); + } + + return result; +} + +rational AudioWaveformCache::length() const +{ + rational len = 0; + + foreach (const TimeRangeWithWaveform &wv, waveforms_) { + len = std::max(len, wv.out()); + } + + return len; +} + +} diff --git a/app/render/audiowaveformcache.h b/app/render/audiowaveformcache.h new file mode 100644 index 000000000..0a27eb175 --- /dev/null +++ b/app/render/audiowaveformcache.h @@ -0,0 +1,85 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2022 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 . + +***/ + +#ifndef AUDIOWAVEFORMCACHE_H +#define AUDIOWAVEFORMCACHE_H + +#include "audio/audiovisualwaveform.h" +#include "playbackcache.h" + +namespace olive { + +class AudioWaveformCache : public PlaybackCache +{ + Q_OBJECT +public: + AudioWaveformCache(QObject *parent = nullptr); + + void WriteWaveform(const TimeRange &range, const TimeRangeList &valid_ranges, const AudioVisualWaveform *waveform); + + const AudioParams &GetParameters() const { return params_; } + void SetParameters(const AudioParams &p) { params_ = p; } + + void Draw(QPainter* painter, const QRect &rect, const double &scale, const rational &start_time) const; + + AudioVisualWaveform::Sample GetSummaryFromTime(const rational &start, const rational &length) const; + + rational length() const; + +private: + class TimeRangeWithWaveform : public TimeRange + { + public: + TimeRangeWithWaveform() = default; + TimeRangeWithWaveform(const TimeRange &r) : + TimeRange(r) + { + } + + void set_in(const rational& in) + { + waveform.TrimIn(in - this->in()); + TimeRange::set_in(in); + } + + void set_out(const rational& out) + { + waveform.Resize(out - this->in()); + TimeRange::set_out(out); + } + + void set_range(const rational& in, const rational& out) + { + waveform.TrimRange(in, out-in); + TimeRange::set_range(in, out); + } + + AudioVisualWaveform waveform; + }; + + QVector waveforms_; + + AudioParams params_; + +}; + +} + +#endif // AUDIOWAVEFORMCACHE_H diff --git a/app/render/previewautocacher.cpp b/app/render/previewautocacher.cpp index ef48d6284..f395fa908 100644 --- a/app/render/previewautocacher.cpp +++ b/app/render/previewautocacher.cpp @@ -80,7 +80,7 @@ RenderTicketPtr PreviewAutoCacher::GetSingleFrame(const rational &t, RenderTicke RenderTicketPtr PreviewAutoCacher::GetRangeOfAudio(TimeRange range, RenderTicketPriority priority) { - return RenderAudio(copied_viewer_node_->GetConnectedSampleOutput(), range, PlaybackCache::kCacheOnly, priority); + return RenderAudio(copied_viewer_node_->GetConnectedSampleOutput(), range, PlaybackCache::kCacheOnly, priority, nullptr); } void PreviewAutoCacher::VideoInvalidatedFromCache(const TimeRange &range) @@ -127,14 +127,17 @@ void PreviewAutoCacher::AudioRendered() SampleBuffer buf = watcher->Get().value(); node->audio_playback_cache()->SetParameters(buf.audio_params()); + node->waveform_cache()->SetParameters(buf.audio_params()); PlaybackCache::RequestType type = PlaybackCache::RequestType(watcher->property("type").toInt()); if (type == PlaybackCache::kCacheOnly) { // WritePCM is tolerant to its buffer being null, it will just write silence instead - node->audio_playback_cache()->WritePCM(range, - valid_ranges, - watcher->Get().value()); + if (AudioPlaybackCache *cache = Node::ValueToPtr(watcher->property("cache"))) { + cache->WritePCM(range, + valid_ranges, + watcher->Get().value()); + } } else { // Detect if this audio was incomplete because it was waiting on a conform to finish if (watcher->GetTicket()->property("incomplete").toBool()) { @@ -146,7 +149,9 @@ void PreviewAutoCacher::AudioRendered() d.needing_conform.insert(range); } } else { - node->audio_playback_cache()->WriteWaveform(range, valid_ranges, &waveform); + if (AudioWaveformCache *cache = Node::ValueToPtr(watcher->property("cache"))) { + cache->WriteWaveform(range, valid_ranges, &waveform); + } } } } @@ -320,16 +325,6 @@ void PreviewAutoCacher::InsertIntoCopyMap(Node *node, Node *copy) void PreviewAutoCacher::ConnectToNodeCache(Node *node) { - connect(node->video_frame_cache(), - &PlaybackCache::AutomaticChanged, - this, - &PreviewAutoCacher::VideoAutoCacheEnableChanged); - - connect(node->audio_playback_cache(), - &PlaybackCache::AutomaticChanged, - this, - &PreviewAutoCacher::AudioAutoCacheEnableChanged); - connect(node->video_frame_cache(), &PlaybackCache::Request, this, @@ -345,32 +340,18 @@ void PreviewAutoCacher::ConnectToNodeCache(Node *node) this, &PreviewAutoCacher::AudioInvalidatedFromCache); + connect(node->waveform_cache(), + &PlaybackCache::Request, + this, + &PreviewAutoCacher::AudioInvalidatedFromCache); + node->video_frame_cache()->LoadState(); node->audio_playback_cache()->LoadState(); node->thumbnail_cache()->LoadState(); - - // Copy invalidated ranges and start rendering if necessary - if (node->video_frame_cache()->IsAutomatic()) { - VideoAutoCacheEnableChangedFromNode(node, true); - } - - if (node->audio_playback_cache()->IsAutomatic()) { - AudioAutoCacheEnableChangedFromNode(node, true); - } } void PreviewAutoCacher::DisconnectFromNodeCache(Node *node) { - disconnect(node->video_frame_cache(), - &PlaybackCache::AutomaticChanged, - this, - &PreviewAutoCacher::VideoAutoCacheEnableChanged); - - disconnect(node->audio_playback_cache(), - &PlaybackCache::AutomaticChanged, - this, - &PreviewAutoCacher::AudioAutoCacheEnableChanged); - disconnect(node->video_frame_cache(), &PlaybackCache::Request, this, @@ -386,17 +367,14 @@ void PreviewAutoCacher::DisconnectFromNodeCache(Node *node) this, &PreviewAutoCacher::AudioInvalidatedFromCache); + disconnect(node->waveform_cache(), + &PlaybackCache::Request, + this, + &PreviewAutoCacher::AudioInvalidatedFromCache); + node->video_frame_cache()->SaveState(); node->audio_playback_cache()->SaveState(); node->thumbnail_cache()->SaveState(); - - if (node->video_frame_cache()->IsAutomatic()) { - VideoAutoCacheEnableChangedFromNode(node, false); - } - - if (node->audio_playback_cache()->IsAutomatic()) { - AudioAutoCacheEnableChangedFromNode(node, false); - } } void PreviewAutoCacher::UpdateGraphChangeValue() @@ -474,24 +452,6 @@ void PreviewAutoCacher::AudioInvalidatedFromNode(Node *node, const TimeRange &ra StartCachingAudioRange(node, range, type); } -void PreviewAutoCacher::VideoAutoCacheEnableChangedFromNode(Node *node, bool e) -{ - if (e) { - VideoInvalidatedList(node, node->video_frame_cache()->GetInvalidatedRanges(node->GetVideoCacheRange())); - } else { - CancelVideoTasks(node); - } -} - -void PreviewAutoCacher::AudioAutoCacheEnableChangedFromNode(Node *node, bool e) -{ - if (e) { - AudioInvalidatedList(node, node->audio_playback_cache()->GetInvalidatedRanges(node->GetAudioCacheRange())); - } else { - CancelAudioTasks(node); - } -} - void PreviewAutoCacher::SetPlayhead(const rational &playhead) { cache_range_ = TimeRange(playhead - OLIVE_CONFIG("DiskCacheBehind").value(), @@ -657,7 +617,13 @@ void PreviewAutoCacher::TryRender() // Start job if (Node *copy = copy_map_.value(d.node)) { - RenderAudio(copy, d.range, d.type, RenderTicketPriority::kNormal); + PlaybackCache *cache; + if (d.type == PlaybackCache::kPreviewsOnly) { + cache = d.node->waveform_cache(); + } else { + cache = d.node->audio_playback_cache(); + } + RenderAudio(copy, d.range, d.type, RenderTicketPriority::kNormal, cache); } else { qCritical() << "Failed to find node copy for audio job"; } @@ -705,12 +671,13 @@ RenderTicketWatcher* PreviewAutoCacher::RenderFrame(Node *node, const rational& return watcher; } -RenderTicketPtr PreviewAutoCacher::RenderAudio(Node *node, const TimeRange &r, PlaybackCache::RequestType type, RenderTicketPriority priority) +RenderTicketPtr PreviewAutoCacher::RenderAudio(Node *node, const TimeRange &r, PlaybackCache::RequestType type, RenderTicketPriority priority, PlaybackCache *cache) { RenderTicketWatcher* watcher = new RenderTicketWatcher(); watcher->setProperty("job", QVariant::fromValue(last_update_time_)); watcher->setProperty("node", Node::PtrToValue(node)); watcher->setProperty("type", type); + watcher->setProperty("cache", Node::PtrToValue(cache)); connect(watcher, &RenderTicketWatcher::Finished, this, &PreviewAutoCacher::AudioRendered); audio_tasks_.insert(watcher, r); @@ -720,6 +687,7 @@ RenderTicketPtr PreviewAutoCacher::RenderAudio(Node *node, const TimeRange &r, P rap.generate_waveforms = (type == PlaybackCache::kPreviewsOnly); rap.priority = priority; + rap.clamp = false; RenderTicketPtr ticket = RenderManager::instance()->RenderAudio(rap); watcher->SetTicket(ticket); @@ -744,20 +712,6 @@ void PreviewAutoCacher::ConformFinished() } } -void PreviewAutoCacher::VideoAutoCacheEnableChanged(bool e) -{ - FrameHashCache *cache = static_cast(sender()); - - VideoAutoCacheEnableChangedFromNode(cache->parent(), e); -} - -void PreviewAutoCacher::AudioAutoCacheEnableChanged(bool e) -{ - AudioPlaybackCache *cache = static_cast(sender()); - - AudioAutoCacheEnableChangedFromNode(cache->parent(), e); -} - void PreviewAutoCacher::CacheProxyTaskCancelled() { pending_video_jobs_.clear(); diff --git a/app/render/previewautocacher.h b/app/render/previewautocacher.h index f987dfe46..1256d8106 100644 --- a/app/render/previewautocacher.h +++ b/app/render/previewautocacher.h @@ -105,7 +105,7 @@ private: RenderTicketWatcher *RenderFrame(Node *node, const rational &time, PlaybackCache::RequestType type, RenderTicketPriority priority, FrameHashCache *cache); - RenderTicketPtr RenderAudio(Node *node, const TimeRange &range, PlaybackCache::RequestType type, RenderTicketPriority priority); + RenderTicketPtr RenderAudio(Node *node, const TimeRange &range, PlaybackCache::RequestType type, RenderTicketPriority priority, PlaybackCache *cache); /** * @brief Process all changes to internal NodeGraph copy @@ -142,9 +142,6 @@ private: void VideoInvalidatedFromNode(Node *node, const olive::TimeRange &range, PlaybackCache::RequestType type); void AudioInvalidatedFromNode(Node *node, const olive::TimeRange &range, PlaybackCache::RequestType type); - void VideoAutoCacheEnableChangedFromNode(Node *node, bool e); - void AudioAutoCacheEnableChangedFromNode(Node *node, bool e); - class QueuedJob { public: enum Type { @@ -264,10 +261,6 @@ private slots: void ConformFinished(); - void VideoAutoCacheEnableChanged(bool e); - - void AudioAutoCacheEnableChanged(bool e); - void CacheProxyTaskCancelled(); }; diff --git a/app/render/rendermanager.cpp b/app/render/rendermanager.cpp index 09d20df00..b86cc64ea 100644 --- a/app/render/rendermanager.cpp +++ b/app/render/rendermanager.cpp @@ -112,6 +112,7 @@ RenderTicketPtr RenderManager::RenderAudio(const RenderAudioParams ¶ms) ticket->setProperty("time", QVariant::fromValue(params.range)); ticket->setProperty("type", kTypeAudio); ticket->setProperty("enablewaveforms", params.generate_waveforms); + ticket->setProperty("clamp", params.clamp); ticket->setProperty("aparam", QVariant::fromValue(params.audio_params)); AddTicket(ticket, params.priority); diff --git a/app/render/rendermanager.h b/app/render/rendermanager.h index 6ef2a503a..6bbe3ba17 100644 --- a/app/render/rendermanager.h +++ b/app/render/rendermanager.h @@ -129,6 +129,7 @@ public: audio_params = aparam; generate_waveforms = false; priority = RenderTicketPriority::kNormal; + clamp = true; } Node *node; @@ -136,6 +137,7 @@ public: AudioParams audio_params; bool generate_waveforms; RenderTicketPriority priority; + bool clamp; }; /** diff --git a/app/render/renderprocessor.cpp b/app/render/renderprocessor.cpp index f2c920422..f774b07af 100644 --- a/app/render/renderprocessor.cpp +++ b/app/render/renderprocessor.cpp @@ -216,7 +216,9 @@ void RenderProcessor::Run() SampleBuffer samples = sample_val.toSamples(); if (samples.is_allocated()) { - samples.clamp(); + if (ticket_->property("clamp").toBool()) { + samples.clamp(); + } if (ticket_->property("enablewaveforms").toBool()) { AudioVisualWaveform vis; diff --git a/app/widget/audiomonitor/audiomonitor.cpp b/app/widget/audiomonitor/audiomonitor.cpp index 8e7c945f5..cfc2ae781 100644 --- a/app/widget/audiomonitor/audiomonitor.cpp +++ b/app/widget/audiomonitor/audiomonitor.cpp @@ -93,11 +93,12 @@ void AudioMonitor::PushSampleBuffer(const SampleBuffer &d) SetUpdateLoop(true); } -void AudioMonitor::StartWaveform(const AudioVisualWaveform *waveform, const rational &start, int playback_speed) +void AudioMonitor::StartWaveform(const AudioWaveformCache *waveform, const rational &start, int playback_speed) { Stop(); - if (start >= waveform->length()) { + waveform_length_ = waveform->length(); + if (start >= waveform_length_) { return; } @@ -239,7 +240,7 @@ void AudioMonitor::paintGL() if (waveform_) { UpdateValuesFromWaveform(v, delta_time); - if (waveform_time_ >= waveform_->length()) { + if (waveform_time_ >= waveform_length_) { Stop(); } } diff --git a/app/widget/audiomonitor/audiomonitor.h b/app/widget/audiomonitor/audiomonitor.h index d29b74491..cf5d2e8f0 100644 --- a/app/widget/audiomonitor/audiomonitor.h +++ b/app/widget/audiomonitor/audiomonitor.h @@ -28,7 +28,7 @@ #include "audio/audiovisualwaveform.h" #include "common/define.h" #include "render/audioparams.h" -#include "render/audioplaybackcache.h" +#include "render/audiowaveformcache.h" namespace olive { @@ -45,7 +45,7 @@ public: return waveform_; } - static void StartWaveformOnAll(const AudioVisualWaveform *waveform, const rational& start, int playback_speed) + static void StartWaveformOnAll(const AudioWaveformCache *waveform, const rational& start, int playback_speed) { foreach (AudioMonitor *m, instances_) { m->StartWaveform(waveform, start, playback_speed); @@ -73,7 +73,7 @@ public slots: void PushSampleBuffer(const SampleBuffer &samples); - void StartWaveform(const AudioVisualWaveform *waveform, const rational& start, int playback_speed); + void StartWaveform(const AudioWaveformCache *waveform, const rational& start, int playback_speed); protected: virtual void paintGL() override; @@ -97,8 +97,9 @@ private: qint64 last_time_; - const AudioVisualWaveform* waveform_; + const AudioWaveformCache* waveform_; rational waveform_time_; + rational waveform_length_; int playback_speed_; diff --git a/app/widget/timelinewidget/tool/pointer.cpp b/app/widget/timelinewidget/tool/pointer.cpp index d0e066493..a05311395 100644 --- a/app/widget/timelinewidget/tool/pointer.cpp +++ b/app/widget/timelinewidget/tool/pointer.cpp @@ -699,7 +699,8 @@ void PointerTool::FinishDrag(TimelineViewMouseEvent *event) // Place the copy instead of the original block block = static_cast(Node::CopyNodeInGraph(block, command)); if (ClipBlock *new_clip = dynamic_cast(block)) { - new_clip->set_waveform(static_cast(p.block)->waveform()); + qDebug() << "FIXME: Copy clip stub"; Q_UNUSED(new_clip) + //new_clip->set_waveform(static_cast(p.block)->waveform()); } } diff --git a/app/widget/timelinewidget/undo/timelineundosplit.cpp b/app/widget/timelinewidget/undo/timelineundosplit.cpp index 74626750c..3900d2b7b 100644 --- a/app/widget/timelinewidget/undo/timelineundosplit.cpp +++ b/app/widget/timelinewidget/undo/timelineundosplit.cpp @@ -44,7 +44,10 @@ void BlockSplitCommand::redo() if (ClipBlock *new_clip = dynamic_cast(new_block_)) { ClipBlock *old_clip = static_cast(block_); - new_clip->set_waveform(old_clip->waveform()); + qDebug() << "FIXME: Copy waveform stub"; + Q_UNUSED(old_clip) + Q_UNUSED(new_clip) + //new_clip->set_waveform(old_clip->waveform()); } // Determine our new lengths diff --git a/app/widget/timelinewidget/view/timelineview.cpp b/app/widget/timelinewidget/view/timelineview.cpp index a11a449ba..7c105e2a8 100644 --- a/app/widget/timelinewidget/view/timelineview.cpp +++ b/app/widget/timelinewidget/view/timelineview.cpp @@ -558,11 +558,11 @@ void TimelineView::DrawBlock(QPainter *painter, bool foreground, Block *block, q // Draw waveform if (clip->GetTrackType() == Track::kAudio && show_waveforms_) { - if (const AudioVisualWaveform *wave = clip->waveform()) { + if (const AudioWaveformCache *wave = clip->waveform()) { rational waveform_start = SceneToTime(block_left - block_in, GetScale(), connected_track_list_->parent()->GetAudioParams().sample_rate_as_time_base()) + media_in; painter->setPen(shadow_color); - AudioVisualWaveform::DrawWaveform(painter, preview_rect, this->GetScale(), *wave, waveform_start); + wave->Draw(painter, preview_rect, this->GetScale(), waveform_start); } } diff --git a/app/widget/viewer/audiowaveformview.cpp b/app/widget/viewer/audiowaveformview.cpp index 92dc7e500..8092cb224 100644 --- a/app/widget/viewer/audiowaveformview.cpp +++ b/app/widget/viewer/audiowaveformview.cpp @@ -47,13 +47,13 @@ AudioWaveformView::AudioWaveformView(QWidget *parent) : setAlignment(Qt::AlignLeft | Qt::AlignTop); } -void AudioWaveformView::SetViewer(AudioPlaybackCache *playback) +void AudioWaveformView::SetViewer(ViewerOutput *playback) { if (playback_) { pool_.clear(); pool_.waitForDone(); - disconnect(playback_, &AudioPlaybackCache::Validated, this, static_cast(&AudioWaveformView::update)); + disconnect(playback_, &ViewerOutput::ConnectedWaveformChanged, this, static_cast(&AudioWaveformView::update)); SetTimebase(0); } @@ -61,9 +61,9 @@ void AudioWaveformView::SetViewer(AudioPlaybackCache *playback) playback_ = playback; if (playback_) { - connect(playback_, &AudioPlaybackCache::Validated, this, static_cast(&AudioWaveformView::update)); + connect(playback_, &ViewerOutput::ConnectedWaveformChanged, this, static_cast(&AudioWaveformView::update)); - SetTimebase(playback_->GetParameters().sample_rate_as_time_base()); + SetTimebase(playback_->GetAudioParams().sample_rate_as_time_base()); } } @@ -75,8 +75,12 @@ void AudioWaveformView::drawForeground(QPainter *p, const QRectF &rect) return; } - const AudioParams& params = playback_->GetParameters(); + const AudioWaveformCache *wave = playback_->GetConnectedWaveform(); + if (!wave) { + return; + } + const AudioParams& params = wave->GetParameters(); if (!params.is_valid()) { return; } @@ -86,7 +90,7 @@ void AudioWaveformView::drawForeground(QPainter *p, const QRectF &rect) // Draw waveform p->setPen(QColor(64, 255, 160)); // FIXME: Hardcoded color - AudioVisualWaveform::DrawWaveform(p, rect.toRect(), GetScale(), playback_->visual(), SceneToTime(GetScroll())); + wave->Draw(p, rect.toRect(), GetScale(), SceneToTime(GetScroll())); // Draw playhead p->setPen(PLAYHEAD_COLOR); diff --git a/app/widget/viewer/audiowaveformview.h b/app/widget/viewer/audiowaveformview.h index 970cb07ad..73162a06a 100644 --- a/app/widget/viewer/audiowaveformview.h +++ b/app/widget/viewer/audiowaveformview.h @@ -36,7 +36,7 @@ class AudioWaveformView : public SeekableWidget public: AudioWaveformView(QWidget* parent = nullptr); - void SetViewer(AudioPlaybackCache *playback); + void SetViewer(ViewerOutput *playback); protected: virtual void drawForeground(QPainter *painter, const QRectF &rect) override; @@ -44,7 +44,7 @@ protected: private: QThreadPool pool_; - AudioPlaybackCache *playback_; + ViewerOutput *playback_; }; diff --git a/app/widget/viewer/viewer.cpp b/app/widget/viewer/viewer.cpp index d0ad7d6e0..371f1e89e 100644 --- a/app/widget/viewer/viewer.cpp +++ b/app/widget/viewer/viewer.cpp @@ -232,7 +232,7 @@ void ViewerWidget::ConnectNodeEvent(ViewerOutput *n) UpdateStack(); - waveform_view_->SetViewer(GetConnectedNode()->audio_playback_cache()); + waveform_view_->SetViewer(GetConnectedNode()); waveform_view_->ConnectTimelinePoints(GetConnectedNode()->GetTimelinePoints()); UpdateRendererVideoParameters(); @@ -947,7 +947,7 @@ void ViewerWidget::FinishPlayPreprocess() } prequeued_audio_.clear(); - AudioMonitor::StartWaveformOnAll(&GetConnectedNode()->audio_playback_cache()->visual(), + AudioMonitor::StartWaveformOnAll(GetConnectedNode()->GetConnectedWaveform(), GetTime(), playback_speed_); }