Merge branch 'master' into nodeview-redux

This commit is contained in:
itsmattkc
2021-06-16 17:20:05 -07:00
23 changed files with 8608 additions and 5122 deletions
+27 -6
View File
@@ -162,6 +162,28 @@ void AudioVisualWaveform::OverwriteSums(const AudioVisualWaveform &sums, const r
length_ = qMax(length_, dest + length); length_ = qMax(length_, dest + length);
} }
void AudioVisualWaveform::OverwriteSilence(const rational &start, const rational &length)
{
for (auto it=mipmapped_data_.begin(); it!=mipmapped_data_.end(); it++) {
rational rate = it->first;
Sample& our_arr = it->second;
double rate_dbl = rate.toDouble();
// Get our destination sample
int our_start_index = time_to_samples(start, rate_dbl);
int our_length_index = time_to_samples(length, rate_dbl);
int our_end_index = our_start_index + our_length_index;
if (our_arr.size() < our_end_index) {
our_arr.resize(our_end_index);
}
memset(reinterpret_cast<char*>(our_arr.data()) + our_start_index, 0, our_length_index);
}
}
void AudioVisualWaveform::Shift(const rational &from, const rational &to) void AudioVisualWaveform::Shift(const rational &from, const rational &to)
{ {
for (auto it=mipmapped_data_.begin(); it!=mipmapped_data_.end(); it++) { for (auto it=mipmapped_data_.begin(); it!=mipmapped_data_.end(); it++) {
@@ -184,9 +206,7 @@ void AudioVisualWaveform::Shift(const rational &from, const rational &to)
// Shifting backwards <- // Shifting backwards <-
int copy_sz = data.size() - from_index; int copy_sz = data.size() - from_index;
for (int i=0; i<copy_sz; i++) { memcpy(&data.data()[to_index], &data.data()[from_index], copy_sz * sizeof(SamplePerChannel));
data.replace(to_index + i, data.at(from_index + i));
}
data.resize(data.size() - (from_index - to_index)); data.resize(data.size() - (from_index - to_index));
} else { } else {
@@ -199,9 +219,10 @@ void AudioVisualWaveform::Shift(const rational &from, const rational &to)
int copy_sz = old_sz - from_index; int copy_sz = old_sz - from_index;
for (int i=0; i<copy_sz; i++) { // Copy to a temporary buffer first to prevent overwriting bytes we need to copy
data.replace(data.size() - i - 1, data.at(old_sz - i - 1)); QByteArray temp(copy_sz * sizeof(SamplePerChannel), Qt::Uninitialized);
} memcpy(temp.data(), &data.data()[from_index], temp.size());
memcpy(&data.data()[to_index], temp.data(), temp.size());
memset(reinterpret_cast<char*>(&data[from_index]), 0, distance * sizeof(SamplePerChannel)); memset(reinterpret_cast<char*>(&data[from_index]), 0, distance * sizeof(SamplePerChannel));
} }
+2
View File
@@ -88,6 +88,8 @@ public:
*/ */
void OverwriteSums(const AudioVisualWaveform& sums, const rational& dest, const rational& offset = 0, const rational &length = 0); void OverwriteSums(const AudioVisualWaveform& sums, const rational& dest, const rational& offset = 0, const rational &length = 0);
void OverwriteSilence(const rational &start, const rational &length);
void Shift(const rational& from, const rational& to); void Shift(const rational& from, const rational& to);
Sample GetSummaryFromTime(const rational& start, const rational& length) const; Sample GetSummaryFromTime(const rational& start, const rational& length) const;
+18 -15
View File
@@ -148,10 +148,14 @@ bool FFmpegDecoder::OpenInternal()
FramePtr FFmpegDecoder::RetrieveVideoInternal(const rational &timecode, const RetrieveVideoParams &params) FramePtr FFmpegDecoder::RetrieveVideoInternal(const rational &timecode, const RetrieveVideoParams &params)
{ {
if (!InitScaler(params)) {
return nullptr;
}
AVStream* s = instance_.avstream(); AVStream* s = instance_.avstream();
// Retrieve frame // Retrieve frame
FFmpegFramePool::ElementPtr return_frame = RetrieveFrame(timecode, params); FFmpegFramePool::ElementPtr return_frame = RetrieveFrame(timecode);
// We found the frame, we'll return a copy // We found the frame, we'll return a copy
if (return_frame) { if (return_frame) {
@@ -161,7 +165,7 @@ FramePtr FFmpegDecoder::RetrieveVideoInternal(const rational &timecode, const Re
native_pix_fmt_, native_pix_fmt_,
native_channel_count_, native_channel_count_,
av_guess_sample_aspect_ratio(instance_.fmt_ctx(), s, nullptr), // May be incorrect, av_guess_sample_aspect_ratio(instance_.fmt_ctx(), s, nullptr), // May be incorrect,
VideoParams::kInterlaceNone, // May be incorrect VideoParams::kInterlaceNone,
filter_params_.divider)); filter_params_.divider));
copy->set_timestamp(timecode); copy->set_timestamp(timecode);
copy->allocate(); copy->allocate();
@@ -182,13 +186,9 @@ void FFmpegDecoder::CloseInternal()
instance_.Close(); instance_.Close();
} }
int FFmpegDecoder::GetFilteredFrame(AVPacket* packet, AVFrame* output_frame, const RetrieveVideoParams& params) int FFmpegDecoder::GetFilteredFrame(AVPacket* packet, AVFrame* output_frame)
{ {
// Ensure scaler is correct for these parameters // Ensure scaler is correct for these parameters
if (!InitScaler(params)) {
return AVERROR(EINVAL);
}
int ret; int ret;
AVFrame* working_frame = av_frame_alloc(); AVFrame* working_frame = av_frame_alloc();
@@ -642,15 +642,18 @@ void FFmpegDecoder::CacheFrameToDisk(AVFrame *f)
void FFmpegDecoder::ClearFrameCache() void FFmpegDecoder::ClearFrameCache()
{ {
cached_frames_.clear(); if (!cached_frames_.isEmpty()) {
cache_at_eof_ = false; cached_frames_.clear();
cache_at_zero_ = false; cache_at_eof_ = false;
cache_at_zero_ = false;
// Filter graph may rely on "continuous" video frames, so we free the scaler here // Filter graph may rely on "continuous" video frames, so we free the scaler here
FreeScaler(); FreeScaler();
InitScaler(filter_params_);
}
} }
FFmpegFramePool::ElementPtr FFmpegDecoder::RetrieveFrame(const rational& time, const RetrieveVideoParams &params) FFmpegFramePool::ElementPtr FFmpegDecoder::RetrieveFrame(const rational& time)
{ {
int64_t target_ts = GetTimeInTimebaseUnits(time, instance_.avstream()->time_base, instance_.avstream()->start_time); int64_t target_ts = GetTimeInTimebaseUnits(time, instance_.avstream()->time_base, instance_.avstream()->start_time);
@@ -658,7 +661,7 @@ FFmpegFramePool::ElementPtr FFmpegDecoder::RetrieveFrame(const rational& time, c
int64_t seek_ts = target_ts; int64_t seek_ts = target_ts;
bool still_seeking = false; bool still_seeking = false;
if (params.src_interlacing != VideoParams::kInterlaceNone) { if (filter_params_.src_interlacing != VideoParams::kInterlaceNone) {
// If we are de-interlacing, the timebase is doubled because we get one frame per field, so we // If we are de-interlacing, the timebase is doubled because we get one frame per field, so we
// double the target timestamp too // double the target timestamp too
target_ts *= 2; target_ts *= 2;
@@ -696,7 +699,7 @@ FFmpegFramePool::ElementPtr FFmpegDecoder::RetrieveFrame(const rational& time, c
// Pull from the decoder // Pull from the decoder
av_frame_unref(working_frame); av_frame_unref(working_frame);
ret = GetFilteredFrame(pkt, working_frame, params); ret = GetFilteredFrame(pkt, working_frame);
// Handle any errors that aren't EOF (EOF is handled later on) // Handle any errors that aren't EOF (EOF is handled later on)
if (ret < 0 && ret != AVERROR_EOF) { if (ret < 0 && ret != AVERROR_EOF) {
+2 -2
View File
@@ -112,7 +112,7 @@ private:
}; };
int GetFilteredFrame(AVPacket *packet, AVFrame *frame, const RetrieveVideoParams &params); int GetFilteredFrame(AVPacket *packet, AVFrame *frame);
/** /**
* @brief Handle an FFmpeg error code * @brief Handle an FFmpeg error code
@@ -138,7 +138,7 @@ private:
void ClearFrameCache(); void ClearFrameCache();
FFmpegFramePool::ElementPtr RetrieveFrame(const rational &time, const RetrieveVideoParams &params); FFmpegFramePool::ElementPtr RetrieveFrame(const rational &time);
void RemoveFirstFrame(); void RemoveFirstFrame();
+1
View File
@@ -30,6 +30,7 @@ namespace olive {
class ExportSubtitlesTab : public QWidget class ExportSubtitlesTab : public QWidget
{ {
Q_OBJECT
public: public:
ExportSubtitlesTab(QWidget *parent = nullptr); ExportSubtitlesTab(QWidget *parent = nullptr);
+7 -9
View File
@@ -47,14 +47,14 @@ ShaderCode MathNodeBase::GetShaderCodeInternal(const QString &shader_id, const Q
const QString& mat_in = (type_a == NodeValue::kTexture) ? param_b_in : param_a_in; const QString& mat_in = (type_a == NodeValue::kTexture) ? param_b_in : param_a_in;
// No-op frag shader (can we return QString() instead?) // No-op frag shader (can we return QString() instead?)
operation = QStringLiteral("texture(%1, ove_texcoord)").arg(tex_in); operation = QStringLiteral("texture2D(%1, ove_texcoord)").arg(tex_in);
vert = QStringLiteral("uniform mat4 %1;\n" vert = QStringLiteral("uniform mat4 %1;\n"
"\n" "\n"
"in vec4 a_position;\n" "attribute vec4 a_position;\n"
"in vec2 a_texcoord;\n" "attribute vec2 a_texcoord;\n"
"\n" "\n"
"out vec2 ove_texcoord;\n" "varying vec2 ove_texcoord;\n"
"\n" "\n"
"void main() {\n" "void main() {\n"
" gl_Position = %1 * a_position;\n" " gl_Position = %1 * a_position;\n"
@@ -96,12 +96,10 @@ ShaderCode MathNodeBase::GetShaderCodeInternal(const QString &shader_id, const Q
frag = QStringLiteral("uniform %1 %3;\n" frag = QStringLiteral("uniform %1 %3;\n"
"uniform %2 %4;\n" "uniform %2 %4;\n"
"\n" "\n"
"in vec2 ove_texcoord;\n" "varying vec2 ove_texcoord;\n"
"\n"
"out vec4 fragColor;\n"
"\n" "\n"
"void main(void) {\n" "void main(void) {\n"
" fragColor = %5;\n" " gl_FragColor = %5;\n"
"}\n").arg(GetShaderUniformType(type_a), "}\n").arg(GetShaderUniformType(type_a),
GetShaderUniformType(type_b), GetShaderUniformType(type_b),
param_a_in, param_a_in,
@@ -128,7 +126,7 @@ QString MathNodeBase::GetShaderUniformType(const olive::NodeValue::Type &type)
QString MathNodeBase::GetShaderVariableCall(const QString &input_id, const NodeValue::Type &type, const QString& coord_op) QString MathNodeBase::GetShaderVariableCall(const QString &input_id, const NodeValue::Type &type, const QString& coord_op)
{ {
if (type == NodeValue::kTexture) { if (type == NodeValue::kTexture) {
return QStringLiteral("texture(%1, ove_texcoord%2)").arg(input_id, coord_op); return QStringLiteral("texture2D(%1, ove_texcoord%2)").arg(input_id, coord_op);
} }
return input_id; return input_id;
+11 -2
View File
@@ -48,6 +48,7 @@ void AudioPlaybackCache::SetParameters(const AudioParams &params)
} }
params_ = params; params_ = params;
visual_.set_channel_count(params_.channel_count());
// Restart empty file so there's always "something" to play // Restart empty file so there's always "something" to play
ClearPlaylist(); ClearPlaylist();
@@ -55,7 +56,7 @@ void AudioPlaybackCache::SetParameters(const AudioParams &params)
emit ParametersChanged(); 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); QList<TimeRange> valid_ranges = GetValidRanges(range, job_time);
if (valid_ranges.isEmpty()) { if (valid_ranges.isEmpty()) {
@@ -83,6 +84,7 @@ void AudioPlaybackCache::WritePCM(const TimeRange &range, SampleBufferPtr sample
foreach (const TimeRange& r, valid_ranges) { foreach (const TimeRange& r, valid_ranges) {
rational this_segment_in = 0; rational this_segment_in = 0;
// Write PCM to playlist
for (auto it=playlist_.begin(); it!=playlist_.end(); it++) { for (auto it=playlist_.begin(); it!=playlist_.end(); it++) {
rational this_segment_out = this_segment_in + params_.bytes_to_time((*it).size()); 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 // Each segment is contiguous, so this out will be the next segment's in
this_segment_in = this_segment_out; 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) { 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 // WritePCM will automatically fill non-existent bytes with silence, so we just have to send
// it an empty sample buffer // 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) void AudioPlaybackCache::ShiftEvent(const rational &from_in_time, const rational &to_in_time)
+9 -1
View File
@@ -21,6 +21,7 @@
#ifndef AUDIOPLAYBACKCACHE_H #ifndef AUDIOPLAYBACKCACHE_H
#define AUDIOPLAYBACKCACHE_H #define AUDIOPLAYBACKCACHE_H
#include "audio/audiovisualwaveform.h"
#include "common/timerange.h" #include "common/timerange.h"
#include "codec/samplebuffer.h" #include "codec/samplebuffer.h"
#include "render/playbackcache.h" #include "render/playbackcache.h"
@@ -65,7 +66,7 @@ public:
void SetParameters(const AudioParams& params); 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); void WriteSilence(const TimeRange &range, qint64 job_time);
@@ -181,6 +182,11 @@ public:
*/ */
PlaybackDevice* CreatePlaybackDevice(QObject *parent = nullptr) const; PlaybackDevice* CreatePlaybackDevice(QObject *parent = nullptr) const;
const AudioVisualWaveform &visual() const
{
return visual_;
}
signals: signals:
void ParametersChanged(); void ParametersChanged();
@@ -212,6 +218,8 @@ private:
AudioParams params_; AudioParams params_;
AudioVisualWaveform visual_;
}; };
} }
+5 -4
View File
@@ -39,9 +39,12 @@ namespace olive {
QMutex FrameHashCache::currently_saving_frames_mutex_; QMutex FrameHashCache::currently_saving_frames_mutex_;
QMap<QByteArray, FramePtr> FrameHashCache::currently_saving_frames_; QMap<QByteArray, FramePtr> FrameHashCache::currently_saving_frames_;
const QString FrameHashCache::kCacheFormatExtension = QStringLiteral(".exr");
#define super PlaybackCache
FrameHashCache::FrameHashCache(QObject *parent) : FrameHashCache::FrameHashCache(QObject *parent) :
PlaybackCache(parent) super(parent)
{ {
if (DiskManager::instance()) { if (DiskManager::instance()) {
connect(DiskManager::instance(), &DiskManager::DeletedFrame, this, &FrameHashCache::HashDeleted); connect(DiskManager::instance(), &DiskManager::DeletedFrame, this, &FrameHashCache::HashDeleted);
@@ -419,11 +422,9 @@ QString FrameHashCache::CachePathName(const QByteArray& hash) const
QString FrameHashCache::CachePathName(const QString &cache_path, const QByteArray &hash) QString FrameHashCache::CachePathName(const QString &cache_path, const QByteArray &hash)
{ {
QString ext = GetFormatExtension();
QDir cache_dir(QDir(cache_path).filePath(QString(hash.left(1).toHex()))); QDir cache_dir(QDir(cache_path).filePath(QString(hash.left(1).toHex())));
QString filename = QStringLiteral("%1%2").arg(QString(hash.mid(1).toHex()), ext); QString filename = QStringLiteral("%1%2").arg(QString(hash.mid(1).toHex()), kCacheFormatExtension);
// Register that in some way this hash has been accessed // Register that in some way this hash has been accessed
QMetaObject::invokeMethod(DiskManager::instance(), QMetaObject::invokeMethod(DiskManager::instance(),
+1 -2
View File
@@ -70,8 +70,6 @@ public:
FramePtr LoadCacheFrame(const QByteArray& hash) const; FramePtr LoadCacheFrame(const QByteArray& hash) const;
static FramePtr LoadCacheFrame(const QString& fn); static FramePtr LoadCacheFrame(const QString& fn);
static QString GetFormatExtension();
static QVector<rational> GetFrameListFromTimeRange(TimeRangeList range_list, const rational& timebase); static QVector<rational> GetFrameListFromTimeRange(TimeRangeList range_list, const rational& timebase);
QVector<rational> GetFrameListFromTimeRange(const TimeRangeList &range); QVector<rational> GetFrameListFromTimeRange(const TimeRangeList &range);
QVector<rational> GetInvalidatedFrames(); QVector<rational> GetInvalidatedFrames();
@@ -94,6 +92,7 @@ private:
static QMutex currently_saving_frames_mutex_; static QMutex currently_saving_frames_mutex_;
static QMap<QByteArray, FramePtr> currently_saving_frames_; static QMap<QByteArray, FramePtr> currently_saving_frames_;
static const QString kCacheFormatExtension;
private slots: private slots:
void HashDeleted(const QString &s, const QByteArray& hash); void HashDeleted(const QString &s, const QByteArray& hash);
+4 -1
View File
@@ -171,8 +171,11 @@ void PreviewAutoCacher::AudioRendered()
if (watcher->HasResult()) { if (watcher->HasResult()) {
const TimeRange &range = audio_tasks_.value(watcher); const TimeRange &range = audio_tasks_.value(watcher);
AudioVisualWaveform waveform = watcher->GetTicket()->property("waveform").value<AudioVisualWaveform>();
viewer_node_->audio_playback_cache()->WritePCM(range, viewer_node_->audio_playback_cache()->WritePCM(range,
watcher->Get().value<SampleBufferPtr>(), watcher->Get().value<SampleBufferPtr>(),
&waveform,
watcher->GetTicket()->GetJobTime()); watcher->GetTicket()->GetJobTime());
bool pcm_is_usable = true; bool pcm_is_usable = true;
@@ -506,7 +509,7 @@ void PreviewAutoCacher::TryRender()
if (!invalidated_audio_.isEmpty()) { if (!invalidated_audio_.isEmpty()) {
foreach (const TimeRange& range, invalidated_audio_) { 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) { foreach (const TimeRange& r, chunks) {
RenderTicketWatcher* watcher = new RenderTicketWatcher(); RenderTicketWatcher* watcher = new RenderTicketWatcher();
+10 -1
View File
@@ -173,7 +173,16 @@ void RenderProcessor::Run()
table = GenerateTable(texture_output.node(), texture_output.output(), time); 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; break;
} }
case RenderManager::kTypeVideoDownload: case RenderManager::kTypeVideoDownload:
+993 -209
View File
File diff suppressed because it is too large Load Diff
+3034 -2090
View File
File diff suppressed because it is too large Load Diff
+2483 -1771
View File
File diff suppressed because it is too large Load Diff
+1968 -933
View File
File diff suppressed because it is too large Load Diff
+7 -8
View File
@@ -266,8 +266,7 @@ Length: %4</source>
<message> <message>
<location filename="../tool/tool.h" line="108"/> <location filename="../tool/tool.h" line="108"/>
<source>Bars</source> <source>Bars</source>
<translatorcomment></translatorcomment> <translation type="unfinished"></translation>
<translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../tool/tool.h" line="110"/> <location filename="../tool/tool.h" line="110"/>
@@ -282,8 +281,7 @@ Length: %4</source>
<message> <message>
<location filename="../tool/tool.h" line="114"/> <location filename="../tool/tool.h" line="114"/>
<source>Tone</source> <source>Tone</source>
<translatorcomment></translatorcomment> <translation type="unfinished"></translation>
<translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../tool/tool.h" line="119"/> <location filename="../tool/tool.h" line="119"/>
@@ -1425,7 +1423,8 @@ Make sure a sequence is loaded and it has a connected Viewer node.</source>
<message> <message>
<location filename="../dialog/export/exportvideotab.cpp" line="57"/> <location filename="../dialog/export/exportvideotab.cpp" line="57"/>
<source>Basic</source> <source>Basic</source>
<translation type="unfinished"></translation> <translatorcomment>Would &quot;&quot; / &quot;&quot; be more appropriate?</translatorcomment>
<translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../dialog/export/exportvideotab.cpp" line="61"/> <location filename="../dialog/export/exportvideotab.cpp" line="61"/>
@@ -2032,7 +2031,7 @@ Make sure a sequence is loaded and it has a connected Viewer node.</source>
<message> <message>
<location filename="../window/mainwindow/mainmenu.cpp" line="664"/> <location filename="../window/mainwindow/mainmenu.cpp" line="664"/>
<source>Cache Entire Sequence</source> <source>Cache Entire Sequence</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../window/mainwindow/mainmenu.cpp" line="665"/> <location filename="../window/mainwindow/mainmenu.cpp" line="665"/>
@@ -2112,7 +2111,7 @@ Make sure a sequence is loaded and it has a connected Viewer node.</source>
<message> <message>
<location filename="../window/mainwindow/mainmenu.cpp" line="685"/> <location filename="../window/mainwindow/mainmenu.cpp" line="685"/>
<source>Enable Snapping</source> <source>Enable Snapping</source>
<translation type="unfinished">/</translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../window/mainwindow/mainmenu.cpp" line="686"/> <location filename="../window/mainwindow/mainmenu.cpp" line="686"/>
@@ -4369,7 +4368,7 @@ What would you like to do with these clips?</source>
<message> <message>
<location filename="../widget/toolbar/toolbar.cpp" line="108"/> <location filename="../widget/toolbar/toolbar.cpp" line="108"/>
<source>Toggle Snapping</source> <source>Toggle Snapping</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
</context> </context>
<context> <context>
+16
View File
@@ -1370,6 +1370,8 @@ public:
Track* track = timeline_->GetTrackAt(track_index_); Track* track = timeline_->GetTrackAt(track_index_);
track->BeginOperation();
bool append = (in_ >= track->track_length()); bool append = (in_ >= track->track_length());
// Check if the placement location is past the end of the timeline // Check if the placement location is past the end of the timeline
@@ -1409,6 +1411,12 @@ public:
} }
} }
track->EndOperation();
if (ripple_remove_command_) {
track->Node::InvalidateCache(TimeRange(insert_->in(), insert_->out()), Track::kBlockInput);
}
for (int i=0; i<position_commands_.size(); i++) { for (int i=0; i<position_commands_.size(); i++) {
position_commands_.at(i)->redo(); position_commands_.at(i)->redo();
} }
@@ -1422,7 +1430,10 @@ public:
Track* t = timeline_->GetTrackAt(track_index_); Track* t = timeline_->GetTrackAt(track_index_);
TimeRange insert_range(insert_->in(), insert_->out());
// Firstly, remove our insert // Firstly, remove our insert
t->BeginOperation();
t->RippleRemoveBlock(insert_); t->RippleRemoveBlock(insert_);
if (ripple_remove_command_) { if (ripple_remove_command_) {
@@ -1432,6 +1443,11 @@ public:
t->RippleRemoveBlock(gap_); t->RippleRemoveBlock(gap_);
gap_->setParent(&memory_manager_); gap_->setParent(&memory_manager_);
} }
t->EndOperation();
if (ripple_remove_command_) {
t->Node::InvalidateCache(insert_range, Track::kBlockInput);
}
// Remove tracks if we added them // Remove tracks if we added them
for (int i=add_track_commands_.size()-1; i>=0; i--) { for (int i=add_track_commands_.size()-1; i>=0; i--) {
+5 -1
View File
@@ -87,7 +87,11 @@ void ImportTool::DragEnter(TimelineViewMouseEvent *event)
PrepGhosts(drag_start_.GetFrame() - parent()->SceneToTime(import_pre_buffer_), PrepGhosts(drag_start_.GetFrame() - parent()->SceneToTime(import_pre_buffer_),
drag_start_.GetTrack().index()); drag_start_.GetTrack().index());
event->accept(); if (parent()->HasGhosts() || !parent()->GetConnectedNode()) {
event->accept();
} else {
event->ignore();
}
} else { } else {
// FIXME: Implement dropping from file // FIXME: Implement dropping from file
event->ignore(); event->ignore();
+3 -47
View File
@@ -60,8 +60,7 @@ void AudioWaveformView::SetViewer(AudioPlaybackCache *playback)
pool_.clear(); pool_.clear();
pool_.waitForDone(); pool_.waitForDone();
disconnect(playback_, &AudioPlaybackCache::Validated, this, &AudioWaveformView::RenderRange); disconnect(playback_, &AudioPlaybackCache::Validated, this, static_cast<void(AudioWaveformView::*)()>(&AudioWaveformView::update));
//disconnect(playback_, &AudioPlaybackCache::ParametersChanged, this, &AudioWaveformView::RenderRange);
SetTimebase(0); SetTimebase(0);
} }
@@ -69,14 +68,9 @@ void AudioWaveformView::SetViewer(AudioPlaybackCache *playback)
playback_ = playback; playback_ = playback;
if (playback_) { if (playback_) {
connect(playback_, &AudioPlaybackCache::Validated, this, &AudioWaveformView::RenderRange); connect(playback_, &AudioPlaybackCache::Validated, this, static_cast<void(AudioWaveformView::*)()>(&AudioWaveformView::update));
//connect(playback_, &AudioPlaybackCache::ParametersChanged, this, &AudioWaveformView::RenderRange);
SetTimebase(playback_->GetParameters().sample_rate_as_time_base()); SetTimebase(playback_->GetParameters().sample_rate_as_time_base());
waveform_.set_channel_count(playback_->GetParameters().channel_count());
RenderRange(TimeRange(0, playback_->GetLength()));
} }
} }
@@ -101,7 +95,7 @@ void AudioWaveformView::paintEvent(QPaintEvent *event)
// Draw waveform // Draw waveform
p.setPen(QColor(64, 255, 160)); // FIXME: Hardcoded color p.setPen(QColor(64, 255, 160)); // FIXME: Hardcoded color
AudioVisualWaveform::DrawWaveform(&p, rect(), GetScale(), waveform_, SceneToTime(GetScroll())); AudioVisualWaveform::DrawWaveform(&p, rect(), GetScale(), playback_->visual(), SceneToTime(GetScroll()));
// Draw playhead // Draw playhead
p.setPen(PLAYHEAD_COLOR); p.setPen(PLAYHEAD_COLOR);
@@ -110,42 +104,4 @@ void AudioWaveformView::paintEvent(QPaintEvent *event)
p.drawLine(playhead_x, 0, playhead_x, height()); p.drawLine(playhead_x, 0, playhead_x, height());
} }
void AudioWaveformView::RenderRange(TimeRange range)
{
// Limit range to length
range = TimeRange(qMax(rational(0), range.in()), qMin(playback_->GetLength(), range.out()));
// Floor to second increments
int64_t start = qFloor(range.in().toDouble());
int64_t end = qCeil(range.out().toDouble());
for (; start!=end; start++) {
TimeRange this_range(start, start+1);
QFutureWatcher<AudioVisualWaveform>* watcher = new QFutureWatcher<AudioVisualWaveform>();
connect(watcher, &QFutureWatcher<AudioVisualWaveform>::finished, this, &AudioWaveformView::BackgroundFinished);
jobs_.insert(this_range, watcher);
watcher->setFuture(QtConcurrent::run(&pool_, GenerateWaveform, playback_->CreatePlaybackDevice(), playback_->GetParameters(), this_range));
}
}
void AudioWaveformView::BackgroundFinished()
{
QFutureWatcher<AudioVisualWaveform>* watcher = static_cast<QFutureWatcher<AudioVisualWaveform>*>(sender());
for (auto it=jobs_.begin(); it!=jobs_.end(); it++) {
if (it.value() == watcher) {
AudioVisualWaveform rendered = watcher->result();
waveform_.OverwriteSums(rendered, it.key().in());
jobs_.erase(it);
update();
break;
}
}
delete watcher;
}
} }
-17
View File
@@ -24,7 +24,6 @@
#include <QtConcurrent/QtConcurrent> #include <QtConcurrent/QtConcurrent>
#include <QWidget> #include <QWidget>
#include "audio/audiovisualwaveform.h"
#include "render/audioparams.h" #include "render/audioparams.h"
#include "render/audioplaybackcache.h" #include "render/audioplaybackcache.h"
#include "widget/timeruler/seekablewidget.h" #include "widget/timeruler/seekablewidget.h"
@@ -37,32 +36,16 @@ class AudioWaveformView : public SeekableWidget
public: public:
AudioWaveformView(QWidget* parent = nullptr); AudioWaveformView(QWidget* parent = nullptr);
//void SetData(const QString& file, const AudioRenderingParams& params);
void SetViewer(AudioPlaybackCache *playback); void SetViewer(AudioPlaybackCache *playback);
const AudioVisualWaveform* waveform() const
{
return &waveform_;
}
protected: protected:
virtual void paintEvent(QPaintEvent* event) override; virtual void paintEvent(QPaintEvent* event) override;
private: private:
void RenderRange(TimeRange range);
QThreadPool pool_; QThreadPool pool_;
AudioPlaybackCache *playback_; AudioPlaybackCache *playback_;
AudioVisualWaveform waveform_;
QHash<TimeRange, QFutureWatcher<AudioVisualWaveform>*> jobs_;
private slots:
void BackgroundFinished();
}; };
} }
+1 -1
View File
@@ -455,7 +455,7 @@ void ViewerWidget::StartAudioOutput()
AudioManager::instance()->StartOutput(audio_cache, AudioManager::instance()->StartOutput(audio_cache,
audio_cache->GetParameters().time_to_bytes(GetTime()), audio_cache->GetParameters().time_to_bytes(GetTime()),
playback_speed_); playback_speed_);
emit AudioManager::instance()->OutputWaveformStarted(waveform_view_->waveform(), emit AudioManager::instance()->OutputWaveformStarted(&audio_cache->visual(),
GetTime(), playback_speed_); GetTime(), playback_speed_);
} }
} }
+1 -2
View File
@@ -639,8 +639,7 @@ void MainMenu::Retranslate()
// Edit menu // Edit menu
edit_menu_->setTitle(tr("&Edit")); edit_menu_->setTitle(tr("&Edit"));
//edit_undo_item_->setText(tr("&Undo")); FIXME: Does Qt translate these automatically? Core::instance()->undo_stack()->UpdateActions(); // Update undo and redo
//edit_redo_item_->setText(tr("Redo"));
edit_delete2_item_->setText(tr("Delete (alt)")); edit_delete2_item_->setText(tr("Delete (alt)"));
edit_insert_item_->setText(tr("Insert")); edit_insert_item_->setText(tr("Insert"));
edit_overwrite_item_->setText(tr("Overwrite")); edit_overwrite_item_->setText(tr("Overwrite"));