renderer: generate audio waveforms in render threads
Solves UI lag issues with long audio sequences.
This commit is contained in:
@@ -48,6 +48,7 @@ void AudioPlaybackCache::SetParameters(const AudioParams ¶ms)
|
||||
}
|
||||
|
||||
params_ = params;
|
||||
visual_.set_channel_count(params_.channel_count());
|
||||
|
||||
// Restart empty file so there's always "something" to play
|
||||
ClearPlaylist();
|
||||
@@ -55,7 +56,7 @@ void AudioPlaybackCache::SetParameters(const AudioParams ¶ms)
|
||||
emit ParametersChanged();
|
||||
}
|
||||
|
||||
void AudioPlaybackCache::WritePCM(const TimeRange &range, SampleBufferPtr samples, const qint64 &job_time)
|
||||
void AudioPlaybackCache::WritePCM(const TimeRange &range, SampleBufferPtr samples, const AudioVisualWaveform *waveform, const qint64 &job_time)
|
||||
{
|
||||
QList<TimeRange> valid_ranges = GetValidRanges(range, job_time);
|
||||
if (valid_ranges.isEmpty()) {
|
||||
@@ -83,6 +84,7 @@ void AudioPlaybackCache::WritePCM(const TimeRange &range, SampleBufferPtr sample
|
||||
foreach (const TimeRange& r, valid_ranges) {
|
||||
rational this_segment_in = 0;
|
||||
|
||||
// Write PCM to playlist
|
||||
for (auto it=playlist_.begin(); it!=playlist_.end(); it++) {
|
||||
rational this_segment_out = this_segment_in + params_.bytes_to_time((*it).size());
|
||||
|
||||
@@ -138,6 +140,13 @@ void AudioPlaybackCache::WritePCM(const TimeRange &range, SampleBufferPtr sample
|
||||
// Each segment is contiguous, so this out will be the next segment's in
|
||||
this_segment_in = this_segment_out;
|
||||
}
|
||||
|
||||
// Write visual
|
||||
if (waveform) {
|
||||
visual_.OverwriteSums(*waveform, r.in(), r.in() - range.in(), r.length());
|
||||
} else {
|
||||
visual_.OverwriteSilence(r.in(), r.length());
|
||||
}
|
||||
}
|
||||
|
||||
foreach (const TimeRange& v, ranges_we_validated) {
|
||||
@@ -149,7 +158,7 @@ void AudioPlaybackCache::WriteSilence(const TimeRange &range, qint64 job_time)
|
||||
{
|
||||
// WritePCM will automatically fill non-existent bytes with silence, so we just have to send
|
||||
// it an empty sample buffer
|
||||
WritePCM(range, nullptr, job_time);
|
||||
WritePCM(range, nullptr, nullptr, job_time);
|
||||
}
|
||||
|
||||
void AudioPlaybackCache::ShiftEvent(const rational &from_in_time, const rational &to_in_time)
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#ifndef AUDIOPLAYBACKCACHE_H
|
||||
#define AUDIOPLAYBACKCACHE_H
|
||||
|
||||
#include "audio/audiovisualwaveform.h"
|
||||
#include "common/timerange.h"
|
||||
#include "codec/samplebuffer.h"
|
||||
#include "render/playbackcache.h"
|
||||
@@ -65,7 +66,7 @@ public:
|
||||
|
||||
void SetParameters(const AudioParams& params);
|
||||
|
||||
void WritePCM(const TimeRange &range, SampleBufferPtr samples, const qint64& job_time);
|
||||
void WritePCM(const TimeRange &range, SampleBufferPtr samples, const AudioVisualWaveform *waveform, const qint64& job_time);
|
||||
|
||||
void WriteSilence(const TimeRange &range, qint64 job_time);
|
||||
|
||||
@@ -181,6 +182,11 @@ public:
|
||||
*/
|
||||
PlaybackDevice* CreatePlaybackDevice(QObject *parent = nullptr) const;
|
||||
|
||||
const AudioVisualWaveform &visual() const
|
||||
{
|
||||
return visual_;
|
||||
}
|
||||
|
||||
signals:
|
||||
void ParametersChanged();
|
||||
|
||||
@@ -212,6 +218,8 @@ private:
|
||||
|
||||
AudioParams params_;
|
||||
|
||||
AudioVisualWaveform visual_;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -171,8 +171,11 @@ void PreviewAutoCacher::AudioRendered()
|
||||
if (watcher->HasResult()) {
|
||||
const TimeRange &range = audio_tasks_.value(watcher);
|
||||
|
||||
AudioVisualWaveform waveform = watcher->GetTicket()->property("waveform").value<AudioVisualWaveform>();
|
||||
|
||||
viewer_node_->audio_playback_cache()->WritePCM(range,
|
||||
watcher->Get().value<SampleBufferPtr>(),
|
||||
&waveform,
|
||||
watcher->GetTicket()->GetJobTime());
|
||||
|
||||
bool pcm_is_usable = true;
|
||||
@@ -506,7 +509,7 @@ void PreviewAutoCacher::TryRender()
|
||||
|
||||
if (!invalidated_audio_.isEmpty()) {
|
||||
foreach (const TimeRange& range, invalidated_audio_) {
|
||||
std::list<TimeRange> chunks = range.Split(2);
|
||||
std::list<TimeRange> chunks = range.Split(30);
|
||||
|
||||
foreach (const TimeRange& r, chunks) {
|
||||
RenderTicketWatcher* watcher = new RenderTicketWatcher();
|
||||
|
||||
@@ -173,7 +173,16 @@ void RenderProcessor::Run()
|
||||
table = GenerateTable(texture_output.node(), texture_output.output(), time);
|
||||
}
|
||||
|
||||
ticket_->Finish(table.Get(NodeValue::kSamples));
|
||||
QVariant sample_variant = table.Get(NodeValue::kSamples);
|
||||
SampleBufferPtr samples = sample_variant.value<SampleBufferPtr>();
|
||||
if (samples && ticket_->property("enablewaveforms").toBool()) {
|
||||
AudioVisualWaveform vis;
|
||||
vis.set_channel_count(samples->audio_params().channel_count());
|
||||
vis.OverwriteSamples(samples, samples->audio_params().sample_rate());
|
||||
ticket_->setProperty("waveform", QVariant::fromValue(vis));
|
||||
}
|
||||
|
||||
ticket_->Finish(sample_variant);
|
||||
break;
|
||||
}
|
||||
case RenderManager::kTypeVideoDownload:
|
||||
|
||||
Reference in New Issue
Block a user