implemented separate waveform cache
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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_;
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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:
|
||||
/**
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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_;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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_;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#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<rational, AudioVisualWaveform::Sample> 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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#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<TimeRangeWithWaveform> waveforms_;
|
||||
|
||||
AudioParams params_;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // AUDIOWAVEFORMCACHE_H
|
||||
@@ -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<SampleBuffer>();
|
||||
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<SampleBuffer>());
|
||||
if (AudioPlaybackCache *cache = Node::ValueToPtr<AudioPlaybackCache>(watcher->property("cache"))) {
|
||||
cache->WritePCM(range,
|
||||
valid_ranges,
|
||||
watcher->Get().value<SampleBuffer>());
|
||||
}
|
||||
} 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<AudioWaveformCache>(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<rational>(),
|
||||
@@ -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<FrameHashCache*>(sender());
|
||||
|
||||
VideoAutoCacheEnableChangedFromNode(cache->parent(), e);
|
||||
}
|
||||
|
||||
void PreviewAutoCacher::AudioAutoCacheEnableChanged(bool e)
|
||||
{
|
||||
AudioPlaybackCache *cache = static_cast<AudioPlaybackCache*>(sender());
|
||||
|
||||
AudioAutoCacheEnableChangedFromNode(cache->parent(), e);
|
||||
}
|
||||
|
||||
void PreviewAutoCacher::CacheProxyTaskCancelled()
|
||||
{
|
||||
pending_video_jobs_.clear();
|
||||
|
||||
@@ -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();
|
||||
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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_;
|
||||
|
||||
|
||||
@@ -699,7 +699,8 @@ void PointerTool::FinishDrag(TimelineViewMouseEvent *event)
|
||||
// Place the copy instead of the original block
|
||||
block = static_cast<Block*>(Node::CopyNodeInGraph(block, command));
|
||||
if (ClipBlock *new_clip = dynamic_cast<ClipBlock*>(block)) {
|
||||
new_clip->set_waveform(static_cast<ClipBlock*>(p.block)->waveform());
|
||||
qDebug() << "FIXME: Copy clip stub"; Q_UNUSED(new_clip)
|
||||
//new_clip->set_waveform(static_cast<ClipBlock*>(p.block)->waveform());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -44,7 +44,10 @@ void BlockSplitCommand::redo()
|
||||
|
||||
if (ClipBlock *new_clip = dynamic_cast<ClipBlock*>(new_block_)) {
|
||||
ClipBlock *old_clip = static_cast<ClipBlock*>(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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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<void(AudioWaveformView::*)()>(&AudioWaveformView::update));
|
||||
disconnect(playback_, &ViewerOutput::ConnectedWaveformChanged, this, static_cast<void(AudioWaveformView::*)()>(&AudioWaveformView::update));
|
||||
|
||||
SetTimebase(0);
|
||||
}
|
||||
@@ -61,9 +61,9 @@ void AudioWaveformView::SetViewer(AudioPlaybackCache *playback)
|
||||
playback_ = playback;
|
||||
|
||||
if (playback_) {
|
||||
connect(playback_, &AudioPlaybackCache::Validated, this, static_cast<void(AudioWaveformView::*)()>(&AudioWaveformView::update));
|
||||
connect(playback_, &ViewerOutput::ConnectedWaveformChanged, this, static_cast<void(AudioWaveformView::*)()>(&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);
|
||||
|
||||
@@ -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_;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -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_);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user