waveform: shift track waveform alongside cache

This commit is contained in:
itsmattkc
2020-06-04 23:32:50 +10:00
parent 0e87762afa
commit 5eeef76b62
10 changed files with 109 additions and 37 deletions
+36
View File
@@ -123,6 +123,42 @@ void AudioVisualWaveform::AppendSilence(const rational &time)
memset(&data_[old_size], 0, (data_.size() - old_size) * sizeof(SamplePerChannel));
}
void AudioVisualWaveform::Shift(const rational &from, const rational &to)
{
int from_index = time_to_samples(from);
int to_index = time_to_samples(to);
if (from_index == to_index) {
return;
}
if (from_index > to_index) {
// Shifting backwards <-
int copy_sz = data_.size() - from_index;
for (int i=0; i<copy_sz; i++) {
data_.replace(to_index + i, data_.at(from_index + i));
}
data_.resize(data_.size() - (from_index - to_index));
} else {
// Shifting forwards ->
int old_sz = data_.size();
int distance = (to_index - from_index);
data_.resize(data_.size() + distance);
int copy_sz = old_sz - from_index;
for (int i=0; i<copy_sz; i++) {
data_.replace(data_.size() - i - 1, data_.at(old_sz - i - 1));
}
memset(&data_[from_index], 0, distance * sizeof(SamplePerChannel));
}
}
QVector<AudioVisualWaveform::SamplePerChannel> AudioVisualWaveform::SumSamples(const float *samples, int nb_samples, int nb_channels)
{
return SumSamplesInternal<float>(samples, nb_samples, nb_channels);
+1
View File
@@ -78,6 +78,7 @@ public:
void TrimOut(const rational& time);
void PrependSilence(const rational& time);
void AppendSilence(const rational& time);
void Shift(const rational& from, const rational& to);
// FIXME: Move to dynamic
static const int kSumSampleRate;
+2 -2
View File
@@ -160,9 +160,9 @@ void TimeRangeList::InsertTimeRange(const TimeRange &range)
append(range);
}
void TimeRangeList::RemoveTimeRange(const TimeRange &range)
void TimeRangeList::RemoveTimeRange(const TimeRange &remove)
{
RemoveTimeRange(this, range);
RemoveTimeRange(this, remove);
}
void TimeRangeList::RemoveTimeRange(QList<TimeRange> *list, const TimeRange &remove)
+1 -1
View File
@@ -73,7 +73,7 @@ public:
void InsertTimeRange(const TimeRange& range);
void RemoveTimeRange(const TimeRange& range);
void RemoveTimeRange(const TimeRange& remove);
static void RemoveTimeRange(QList<TimeRange>* list, const TimeRange& remove);
+21 -9
View File
@@ -57,9 +57,6 @@ ViewerOutput::ViewerOutput()
// Create UUID for this node
uuid_ = QUuid::createUuid();
connect(this, &ViewerOutput::LengthChanged, &video_frame_cache_, &PlaybackCache::SetLength);
connect(this, &ViewerOutput::LengthChanged, &audio_playback_cache_, &PlaybackCache::SetLength);
}
Node *ViewerOutput::copy() const
@@ -87,10 +84,24 @@ QString ViewerOutput::Description() const
return tr("Interface between a Viewer panel and the node system.");
}
void ViewerOutput::ShiftCache(const rational &from, const rational &to)
void ViewerOutput::ShiftVideoCache(const rational &from, const rational &to)
{
video_frame_cache_.Shift(from, to);
}
void ViewerOutput::ShiftAudioCache(const rational &from, const rational &to)
{
audio_playback_cache_.Shift(from, to);
foreach (TrackOutput* track, track_lists_.at(Timeline::kTrackTypeAudio)->GetTracks()) {
track->waveform().Shift(from, to);
}
}
void ViewerOutput::ShiftCache(const rational &from, const rational &to)
{
ShiftVideoCache(from, to);
ShiftAudioCache(from, to);
}
void ViewerOutput::InvalidateCache(const TimeRange &range, NodeInput *from, NodeInput *source)
@@ -179,13 +190,14 @@ void ViewerOutput::VerifyLength()
audio_length = t.Get(NodeParam::kNumber, "length").value<rational>();
}
rational timeline_length;
video_length = qMax(video_length, track_lists_.at(Timeline::kTrackTypeVideo)->GetTotalLength());
audio_length = qMax(audio_length, track_lists_.at(Timeline::kTrackTypeAudio)->GetTotalLength());
rational subtitle_length = track_lists_.at(Timeline::kTrackTypeSubtitle)->GetTotalLength();
foreach (TrackList* tl, track_lists_) {
timeline_length = qMax(timeline_length, tl->GetTotalLength());
}
video_frame_cache_.SetLength(video_length);
audio_playback_cache_.SetLength(audio_length);
rational real_length = qMax(timeline_length, qMax(video_length, audio_length));
rational real_length = qMax(subtitle_length, qMax(video_length, audio_length));
if (real_length != last_length_) {
last_length_ = real_length;
+2
View File
@@ -54,6 +54,8 @@ public:
virtual QList<CategoryID> Category() const override;
virtual QString Description() const override;
void ShiftVideoCache(const rational& from, const rational& to);
void ShiftAudioCache(const rational& from, const rational& to);
void ShiftCache(const rational& from, const rational& to);
NodeInput* texture_input() const {
+15 -6
View File
@@ -131,10 +131,19 @@ void AudioPlaybackCache::WriteSilence(const TimeRange &range)
void AudioPlaybackCache::ShiftEvent(const rational &from, const rational &to)
{
qint64 from_offset = params_.time_to_bytes(from);
qint64 to_offset = params_.time_to_bytes(to);
if (from_offset == to_offset) {
return;
}
QFile f(filename_);
if (f.open(QFile::ReadWrite)) {
qint64 from_offset = params_.time_to_bytes(from);
qint64 to_offset = params_.time_to_bytes(to);
if (!f.size()) {
return;
}
qint64 chunk = qAbs(to_offset - from_offset);
QByteArray buf(chunk, Qt::Uninitialized);
@@ -150,7 +159,7 @@ void AudioPlaybackCache::ShiftEvent(const rational &from, const rational &to)
qint64 write_offset = f.size();
do {
while (read_offset != from_offset) {
// Calculate how much will be read this time
qint64 chunk_sz = qMin(chunk, read_offset - from_offset);
@@ -165,7 +174,7 @@ void AudioPlaybackCache::ShiftEvent(const rational &from, const rational &to)
f.seek(write_offset);
f.write(buf.data(), chunk_sz);
} while (read_offset != from_offset);
}
// Replace remainder with silence
f.seek(from_offset);
@@ -175,7 +184,7 @@ void AudioPlaybackCache::ShiftEvent(const rational &from, const rational &to)
} else {
// Shifting backwards, we will shift bytes and truncate
do {
while (from_offset != f.size()) {
// Read region to be shifted
f.seek(from_offset);
qint64 read_sz = f.read(buf.data(), buf.size());
@@ -184,7 +193,7 @@ void AudioPlaybackCache::ShiftEvent(const rational &from, const rational &to)
// Write it at the destination
f.seek(to_offset);
to_offset += f.write(buf, read_sz);
} while (from_offset != f.size());
}
// Truncate
f.resize(f.size() - chunk);
+4 -1
View File
@@ -353,6 +353,10 @@ void RenderBackend::WorkerFinished()
void RenderBackend::WorkerGeneratedWaveform(const TrackOutput *copied_track, const AudioVisualWaveform& samples, const rational &r)
{
if (!viewer_node_) {
return;
}
TrackOutput* track = nullptr;
/*
@@ -372,7 +376,6 @@ void RenderBackend::WorkerGeneratedWaveform(const TrackOutput *copied_track, con
if (track) {
track->waveform().set_channel_count(audio_params_.channel_count());
track->waveform().OverwriteSums(samples, r);
qDebug() << "Preview changed";
emit track->PreviewChanged();
} else {
qDebug() << "Failed to find track";
+11 -2
View File
@@ -57,6 +57,10 @@ void PlaybackCache::SetLength(const rational &r)
void PlaybackCache::Shift(const rational &from, const rational &to)
{
if (from == to) {
return;
}
QMutexLocker locker(&lock_);
// An region between `from` and `to` will be inserted or spliced out
@@ -75,6 +79,8 @@ void PlaybackCache::Shift(const rational &from, const rational &to)
ShiftEvent(from, to);
length_ += diff;
if (diff > rational()) {
// If shifting forward, add this section to the invalidated region
TimeRange invalidate_range(from, to);
@@ -113,12 +119,15 @@ void PlaybackCache::NoLockSetLength(const rational &r)
return;
}
TimeRange range_diff(length_, r);
if (r > length_) {
// If new length is greater, simply extend the invalidated range for now
invalidated_.InsertTimeRange(TimeRange(length_, r));
invalidated_.InsertTimeRange(range_diff);
} else {
// If new length is smaller, removed hashes
invalidated_.RemoveTimeRange(TimeRange(r, length_));
invalidated_.RemoveTimeRange(range_diff);
RemoveRangeFromJobs(range_diff);
}
LengthChangedEvent(length_, r);
+16 -16
View File
@@ -1226,6 +1226,12 @@ void TrackListRippleRemoveAreaCommand::redo_internal()
if (all_tracks_unlocked_) {
// We can optimize here by simply shifting the whole cache forward instead of re-caching
// everything following this time
if (list_->type() == Timeline::kTrackTypeVideo) {
static_cast<ViewerOutput*>(list_->parent())->ShiftVideoCache(out_, in_);
} else if (list_->type() == Timeline::kTrackTypeAudio) {
static_cast<ViewerOutput*>(list_->parent())->ShiftAudioCache(out_, in_);
}
foreach (TrackOutput* track, working_tracks_) {
track->BlockInvalidateCache();
}
@@ -1236,13 +1242,6 @@ void TrackListRippleRemoveAreaCommand::redo_internal()
}
if (all_tracks_unlocked_) {
// Shift cache
if (list_->type() == Timeline::kTrackTypeVideo) {
static_cast<ViewerOutput*>(list_->parent())->video_frame_cache()->Shift(out_, in_);
} else if (list_->type() == Timeline::kTrackTypeAudio) {
static_cast<ViewerOutput*>(list_->parent())->audio_playback_cache()->Shift(out_, in_);
}
foreach (TrackOutput* track, working_tracks_) {
track->UnblockInvalidateCache();
track->PushLengthChangeSignal();
@@ -1255,6 +1254,12 @@ void TrackListRippleRemoveAreaCommand::undo_internal()
if (all_tracks_unlocked_) {
// We can optimize here by simply shifting the whole cache forward instead of re-caching
// everything following this time
if (list_->type() == Timeline::kTrackTypeVideo) {
static_cast<ViewerOutput*>(list_->parent())->ShiftVideoCache(in_, out_);
} else if (list_->type() == Timeline::kTrackTypeAudio) {
static_cast<ViewerOutput*>(list_->parent())->ShiftAudioCache(in_, out_);
}
foreach (TrackOutput* track, working_tracks_) {
track->BlockInvalidateCache();
}
@@ -1265,17 +1270,10 @@ void TrackListRippleRemoveAreaCommand::undo_internal()
}
if (all_tracks_unlocked_) {
// Shift cache back
foreach (TrackOutput* track, working_tracks_) {
track->UnblockInvalidateCache();
track->PushLengthChangeSignal();
}
if (list_->type() == Timeline::kTrackTypeVideo) {
static_cast<ViewerOutput*>(list_->parent())->video_frame_cache()->Shift(in_, out_);
} else if (list_->type() == Timeline::kTrackTypeAudio) {
static_cast<ViewerOutput*>(list_->parent())->audio_playback_cache()->Shift(in_, out_);
}
}
}
@@ -1387,9 +1385,9 @@ void TrackListRippleToolCommand::redo_internal()
}
if (track_list_->type() == Timeline::kTrackTypeVideo) {
static_cast<ViewerOutput*>(track_list_->parent())->video_frame_cache()->Shift(old_latest_pt, new_latest_pt);
static_cast<ViewerOutput*>(track_list_->parent())->ShiftVideoCache(old_latest_pt, new_latest_pt);
} else if (track_list_->type() == Timeline::kTrackTypeAudio) {
static_cast<ViewerOutput*>(track_list_->parent())->audio_playback_cache()->Shift(old_latest_pt, new_latest_pt);
static_cast<ViewerOutput*>(track_list_->parent())->ShiftAudioCache(old_latest_pt, new_latest_pt);
}
foreach (const RippleInfo& info, info_) {
@@ -1400,6 +1398,8 @@ void TrackListRippleToolCommand::redo_internal()
info.track->InvalidateCache(TimeRange(earliest_pt, new_latest_pt),
info.track->block_input(),
info.track->block_input());
} else {
info.track->PushLengthChangeSignal();
}
}
}