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_);
}