diff --git a/app/audio/audiovisualwaveform.cpp b/app/audio/audiovisualwaveform.cpp index 0e005bcb7..92b7e5d49 100644 --- a/app/audio/audiovisualwaveform.cpp +++ b/app/audio/audiovisualwaveform.cpp @@ -233,6 +233,29 @@ void AudioVisualWaveform::Shift(const rational &from, const rational &to) length_ += (to-from); } +void AudioVisualWaveform::TrimIn(const rational &length) +{ + 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); + + if (chop_length == 0) { + continue; + } + + if (chop_length > 0) { + data = data.mid(chop_length); + } else { + data.insert(0, -chop_length, SamplePerChannel()); + } + } + + length_ -= 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 f4be103e9..eb8847a51 100644 --- a/app/audio/audiovisualwaveform.h +++ b/app/audio/audiovisualwaveform.h @@ -92,6 +92,8 @@ public: void Shift(const rational& from, const rational& to); + void TrimIn(const rational &length); + Sample GetSummaryFromTime(const rational& start, const rational& length) const; static Sample SumSamples(const float* samples, int nb_samples, int nb_channels); diff --git a/app/node/block/block.h b/app/node/block/block.h index e438829d2..0d3b86f72 100644 --- a/app/node/block/block.h +++ b/app/node/block/block.h @@ -129,6 +129,8 @@ signals: void LengthChanged(); + void PreviewChanged(); + protected: virtual void InputValueChangedEvent(const QString& input, int element) override; diff --git a/app/node/block/clip/clip.cpp b/app/node/block/clip/clip.cpp index e8230d3c7..edff8f436 100644 --- a/app/node/block/clip/clip.cpp +++ b/app/node/block/clip/clip.cpp @@ -196,6 +196,19 @@ void ClipBlock::LinkChangeEvent() } } +void ClipBlock::InputValueChangedEvent(const QString &input, int element) +{ + super::InputValueChangedEvent(input, element); + + if (input == kMediaInInput) { + // Shift waveform in the inverse that the media in moved + rational diff = media_in() - last_media_in_; + waveform_.TrimIn(diff); + + last_media_in_ = media_in(); + } +} + TimeRange ClipBlock::InputTimeAdjustment(const QString& input, int element, const TimeRange& input_time) const { Q_UNUSED(element) diff --git a/app/node/block/clip/clip.h b/app/node/block/clip/clip.h index 8c4448bd9..270df5d9d 100644 --- a/app/node/block/clip/clip.h +++ b/app/node/block/clip/clip.h @@ -21,6 +21,7 @@ #ifndef CLIPBLOCK_H #define CLIPBLOCK_H +#include "audio/audiovisualwaveform.h" #include "node/block/block.h" namespace olive { @@ -95,6 +96,11 @@ public: return block_links_; } + AudioVisualWaveform& waveform() + { + return waveform_; + } + static const QString kBufferIn; static const QString kMediaInInput; static const QString kSpeedInput; @@ -103,6 +109,8 @@ public: protected: virtual void LinkChangeEvent() override; + virtual void InputValueChangedEvent(const QString &input, int element) override; + private: rational SequenceToMediaTime(const rational& sequence_time, bool ignore_reverse = false) const; @@ -113,6 +121,12 @@ private: TransitionBlock* in_transition_; TransitionBlock* out_transition_; +private: + AudioVisualWaveform waveform_; + + rational last_media_in_; + + }; } diff --git a/app/node/output/track/track.h b/app/node/output/track/track.h index 56e0ef82d..3b3abc10b 100644 --- a/app/node/output/track/track.h +++ b/app/node/output/track/track.h @@ -21,7 +21,6 @@ #ifndef TRACK_H #define TRACK_H -#include "audio/audiovisualwaveform.h" #include "node/block/block.h" #include "timeline/timelinecommon.h" @@ -364,11 +363,6 @@ public: virtual void Hash(const QString& output, QCryptographicHash& hash, const rational &time, const VideoParams& video_params) const override; - AudioVisualWaveform& waveform() - { - return waveform_; - } - static const double kTrackHeightDefault; static const double kTrackHeightMinimum; static const double kTrackHeightInterval; @@ -412,11 +406,6 @@ signals: */ void IndexChanged(int old, int now); - /** - * @brief Signal emitted when preview (waveform) has changed and UI should be updated - */ - void PreviewChanged(); - /** * @brief Emitted when a block changes length and all the subsequent blocks had to update */ @@ -455,8 +444,6 @@ private: bool locked_; - AudioVisualWaveform waveform_; - private slots: void BlockLengthChanged(); diff --git a/app/node/project/sequence/sequence.cpp b/app/node/project/sequence/sequence.cpp index 5773e42c5..4c05bb501 100644 --- a/app/node/project/sequence/sequence.cpp +++ b/app/node/project/sequence/sequence.cpp @@ -163,13 +163,6 @@ void Sequence::InputDisconnectedEvent(const QString &input, int element, const N super::InputDisconnectedEvent(input, element, output); } -void Sequence::ShiftAudioEvent(const rational &from, const rational &to) -{ - foreach (Track* track, track_lists_.at(Track::kAudio)->GetTracks()) { - track->waveform().Shift(from, to); - } -} - void Sequence::UpdateTrackCache() { track_cache_.clear(); diff --git a/app/node/project/sequence/sequence.h b/app/node/project/sequence/sequence.h index 6e638cb19..fd50b9e92 100644 --- a/app/node/project/sequence/sequence.h +++ b/app/node/project/sequence/sequence.h @@ -97,8 +97,6 @@ public: } protected: - virtual void ShiftAudioEvent(const rational &from, const rational &to) override; - virtual void InputConnectedEvent(const QString &input, int element, const NodeOutput &output) override; virtual void InputDisconnectedEvent(const QString &input, int element, const NodeOutput &output) override; diff --git a/app/render/previewautocacher.cpp b/app/render/previewautocacher.cpp index fa54b5c53..4149a33b4 100644 --- a/app/render/previewautocacher.cpp +++ b/app/render/previewautocacher.cpp @@ -185,24 +185,28 @@ void PreviewAutoCacher::AudioRendered() QVector waveform_list = watcher->GetTicket()->property("waveforms").value< QVector >(); foreach (const RenderProcessor::RenderedWaveform& waveform_info, waveform_list) { // Find original track - Track* track = nullptr; + ClipBlock* block = nullptr; for (auto it=copy_map_.cbegin(); it!=copy_map_.cend(); it++) { - if (it.value() == waveform_info.track) { - track = static_cast(it.key()); + if (it.value() == waveform_info.block) { + block = static_cast(it.key()); break; } } - if (track && !valid_ranges.isEmpty()) { + if (block && !valid_ranges.isEmpty()) { // Generate visual waveform in this background thread - track->waveform().set_channel_count(viewer_node_->GetAudioParams().channel_count()); + block->waveform().set_channel_count(viewer_node_->GetAudioParams().channel_count()); - foreach (const TimeRange& r, valid_ranges) { - track->waveform().OverwriteSums(waveform_info.waveform, r.in(), r.in() - waveform_info.range.in(), r.length()); + // Determine which of the waveform ranges we got intersects with the valid ranges + TimeRangeList intersections = valid_ranges.Intersects(waveform_info.range + block->in()); + foreach (TimeRange r, intersections) { + // For each range, adjust it relative to the block and write it + r -= block->in(); + block->waveform().OverwriteSums(waveform_info.waveform, r.in(), r.in(), r.length()); } - emit track->PreviewChanged(); + emit block->PreviewChanged(); } } } diff --git a/app/render/renderprocessor.cpp b/app/render/renderprocessor.cpp index 4f65968fb..7901a8232 100644 --- a/app/render/renderprocessor.cpp +++ b/app/render/renderprocessor.cpp @@ -272,8 +272,7 @@ NodeValueTable RenderProcessor::GenerateBlockTable(const Track *track, const Tim } // If this is a clip, we might have extra speed/reverse information - ClipBlock *clip_cast = dynamic_cast(b); - if (clip_cast) { + if (ClipBlock *clip_cast = dynamic_cast(b)) { double speed_value = clip_cast->speed(); bool reversed = clip_cast->reverse(); @@ -288,6 +287,20 @@ NodeValueTable RenderProcessor::GenerateBlockTable(const Track *track, const Tim if (reversed) { samples_from_this_block->reverse(); } + + // Create block waveforms if requested + if (ticket_->property("enablewaveforms").toBool()) { + // Generate a visual waveform from the samples acquired from this block + AudioVisualWaveform visual_waveform; + visual_waveform.set_channel_count(audio_params.channel_count()); + visual_waveform.OverwriteSamples(samples_from_this_block, audio_params.sample_rate()); + + // Format it for use back int eh maint hread + RenderedWaveform waveform_info = {clip_cast, visual_waveform, range_for_block - b->in()}; + QVector waveform_list = ticket_->property("waveforms").value< QVector >(); + waveform_list.append(waveform_info); + ticket_->setProperty("waveforms", QVariant::fromValue(waveform_list)); + } } int copy_length = qMin(max_dest_sz, samples_from_this_block->sample_count()); @@ -301,18 +314,6 @@ NodeValueTable RenderProcessor::GenerateBlockTable(const Track *track, const Tim } } - if (ticket_->property("enablewaveforms").toBool()) { - // Generate a visual waveform and send it back to the main thread - AudioVisualWaveform visual_waveform; - visual_waveform.set_channel_count(audio_params.channel_count()); - visual_waveform.OverwriteSamples(block_range_buffer, audio_params.sample_rate()); - - RenderedWaveform waveform_info = {track, visual_waveform, range}; - QVector waveform_list = ticket_->property("waveforms").value< QVector >(); - waveform_list.append(waveform_info); - ticket_->setProperty("waveforms", QVariant::fromValue(waveform_list)); - } - merged_table.Push(NodeValue::kSamples, QVariant::fromValue(block_range_buffer), track); return merged_table; diff --git a/app/render/renderprocessor.h b/app/render/renderprocessor.h index c3dd9e91a..bf2635284 100644 --- a/app/render/renderprocessor.h +++ b/app/render/renderprocessor.h @@ -21,6 +21,7 @@ #ifndef RENDERPROCESSOR_H #define RENDERPROCESSOR_H +#include "node/block/clip/clip.h" #include "node/traverser.h" #include "render/renderer.h" #include "rendercache.h" @@ -35,7 +36,7 @@ public: static void Process(RenderTicketPtr ticket, Renderer* render_ctx, StillImageCache* still_image_cache, DecoderCache* decoder_cache, ShaderCache* shader_cache, QVariant default_shader); struct RenderedWaveform { - const Track* track; + const ClipBlock* block; AudioVisualWaveform waveform; TimeRange range; }; diff --git a/app/widget/timelinewidget/timelinewidget.cpp b/app/widget/timelinewidget/timelinewidget.cpp index eabc90be2..bc2624f57 100644 --- a/app/widget/timelinewidget/timelinewidget.cpp +++ b/app/widget/timelinewidget/timelinewidget.cpp @@ -893,6 +893,7 @@ void TimelineWidget::AddBlock(Block *block) connect(block, &Block::LabelChanged, this, &TimelineWidget::BlockUpdated); connect(block, &Block::ColorChanged, this, &TimelineWidget::BlockUpdated); connect(block, &Block::EnabledChanged, this, &TimelineWidget::BlockUpdated); + connect(block, &Block::PreviewChanged, this, &TimelineWidget::BlockUpdated); added_blocks_.append(block); } @@ -905,6 +906,7 @@ void TimelineWidget::RemoveBlock(Block *block) disconnect(block, &Block::LabelChanged, this, &TimelineWidget::BlockUpdated); disconnect(block, &Block::ColorChanged, this, &TimelineWidget::BlockUpdated); disconnect(block, &Block::EnabledChanged, this, &TimelineWidget::BlockUpdated); + disconnect(block, &Block::PreviewChanged, this, &TimelineWidget::BlockUpdated); // Take item from map added_blocks_.removeOne(block); @@ -927,7 +929,6 @@ void TimelineWidget::AddTrack(Track *track) connect(track, &Track::IndexChanged, this, &TimelineWidget::TrackUpdated); connect(track, &Track::IndexChanged, this, &TimelineWidget::TrackIndexChanged); - connect(track, &Track::PreviewChanged, this, &TimelineWidget::TrackUpdated); connect(track, &Track::BlocksRefreshed, this, &TimelineWidget::TrackUpdated); connect(track, &Track::TrackHeightChangedInPixels, this, &TimelineWidget::TrackUpdated); connect(track, &Track::BlockAdded, this, &TimelineWidget::AddBlock); @@ -938,7 +939,6 @@ void TimelineWidget::RemoveTrack(Track *track) { disconnect(track, &Track::IndexChanged, this, &TimelineWidget::TrackUpdated); disconnect(track, &Track::IndexChanged, this, &TimelineWidget::TrackIndexChanged); - disconnect(track, &Track::PreviewChanged, this, &TimelineWidget::TrackUpdated); disconnect(track, &Track::BlocksRefreshed, this, &TimelineWidget::TrackUpdated); disconnect(track, &Track::TrackHeightChangedInPixels, this, &TimelineWidget::TrackUpdated); disconnect(track, &Track::BlockAdded, this, &TimelineWidget::AddBlock); diff --git a/app/widget/timelinewidget/view/timelineview.cpp b/app/widget/timelinewidget/view/timelineview.cpp index e07c981f6..0804d1800 100644 --- a/app/widget/timelinewidget/view/timelineview.cpp +++ b/app/widget/timelinewidget/view/timelineview.cpp @@ -403,7 +403,9 @@ void TimelineView::DrawBlocks(QPainter *painter, bool foreground) while (block) { if (dynamic_cast(block) || dynamic_cast(block)) { - qreal block_left = qMax(left_bound, TimeToScene(block->in())); + qreal block_in = TimeToScene(block->in()); + + qreal block_left = qMax(left_bound, block_in); qreal block_right = qMin(right_bound, TimeToScene(block->out())) - 1; qreal block_top = GetTrackY(track->Index()); qreal block_height = GetTrackHeight(track->Index()); @@ -454,15 +456,16 @@ void TimelineView::DrawBlocks(QPainter *painter, bool foreground) // Draw waveform if (show_waveforms_) { - QRect waveform_rect = r.adjusted(0, text_total_height, 0, 0).toRect(); - painter->setPen(shadow_color); - AudioVisualWaveform::DrawWaveform(painter, waveform_rect, this->GetScale(), track->waveform(), - SceneToTime(block_left, GetScale(), connected_track_list_->parent()->GetAudioParams().sample_rate_as_time_base())); + if (ClipBlock *clip = dynamic_cast(block)) { + QRect waveform_rect = r.adjusted(0, text_total_height, 0, 0).toRect(); + painter->setPen(shadow_color); + AudioVisualWaveform::DrawWaveform(painter, waveform_rect, this->GetScale(), clip->waveform(), + SceneToTime(block_left - block_in, GetScale(), connected_track_list_->parent()->GetAudioParams().sample_rate_as_time_base())); + } } // For transitions, show lines representing a transition - TransitionBlock* transition = dynamic_cast(block); - if (transition) { + if (TransitionBlock* transition = dynamic_cast(block)) { QVector lines; if (transition->connected_in_block()) {