implemented "value hints" for choosing output values to use

This commit is contained in:
itsmattkc
2021-09-17 20:56:53 -07:00
parent a5e7859e1f
commit 67c7c71ca2
30 changed files with 439 additions and 85 deletions
+1 -1
View File
@@ -100,7 +100,7 @@ void Block::InputValueChangedEvent(const QString &input, int element)
}
}
bool Block::HashPassthrough(const QString &input, const Node::ValueHint &output, QCryptographicHash &hash, const NodeGlobals &globals, const VideoParams &video_params) const
bool Block::HashPassthrough(const QString &input, QCryptographicHash &hash, const NodeGlobals &globals, const VideoParams &video_params) const
{
if (IsInputConnected(input)) {
TimeRange t = InputTimeAdjustment(input, -1, globals.time());
+1 -1
View File
@@ -134,7 +134,7 @@ signals:
protected:
virtual void InputValueChangedEvent(const QString& input, int element) override;
bool HashPassthrough(const QString &input, const ValueHint &output, QCryptographicHash &hash, const NodeGlobals &globals, const VideoParams& video_params) const;
bool HashPassthrough(const QString &input, QCryptographicHash &hash, const NodeGlobals &globals, const VideoParams& video_params) const;
Block* previous_;
Block* next_;
+1 -1
View File
@@ -266,7 +266,7 @@ void ClipBlock::Retranslate()
void ClipBlock::Hash(const ValueHint &out, QCryptographicHash &hash, const NodeGlobals &globals, const VideoParams &video_params) const
{
HashPassthrough(kBufferIn, GetValueHintForInput(kBufferIn, -1), hash, globals, video_params);
HashPassthrough(kBufferIn, hash, globals, video_params);
}
}
+3 -3
View File
@@ -139,9 +139,9 @@ double TransitionBlock::GetInProgress(const double &time) const
void TransitionBlock::Hash(const ValueHint &output, QCryptographicHash &hash, const NodeGlobals &globals, const VideoParams &video_params) const
{
if (HashPassthrough(kInBlockInput, GetValueHintForInput(kInBlockInput, -1), hash, globals, video_params)
|| HashPassthrough(kOutBlockInput, GetValueHintForInput(kOutBlockInput, -1), hash, globals, video_params)) {
HashAddNodeSignature(hash, output);
if (HashPassthrough(kInBlockInput, hash, globals, video_params)
|| HashPassthrough(kOutBlockInput, hash, globals, video_params)) {
HashAddNodeSignature(hash);
double time_dbl = globals.time().in().toDouble();
double all_prog = GetTotalProgress(time_dbl);
@@ -61,17 +61,15 @@ void TransformDistortNode::Value(const NodeValueRow &value, const NodeGlobals &g
// Pop texture
NodeValue texture_meta = value[kTextureInput];
TexturePtr texture = texture_meta.data().value<TexturePtr>();
bool pushed_job = false;
// If we have a texture, generate a matrix and make it happen
if (texture) {
if (TexturePtr texture = texture_meta.data().value<TexturePtr>()) {
// Adjust our matrix by the resolutions involved
QMatrix4x4 real_matrix = GenerateAutoScaledMatrix(generated_matrix, value, globals, texture->params());
if (real_matrix.isIdentity()) {
// We don't expect any changes, just push as normal
table->Push(texture_meta);
} else {
if (!real_matrix.isIdentity()) {
// The matrix will transform things
ShaderJob job;
job.InsertValue(QStringLiteral("ove_maintex"), NodeValue(NodeValue::kTexture, QVariant::fromValue(texture), this));
@@ -83,8 +81,15 @@ void TransformDistortNode::Value(const NodeValueRow &value, const NodeGlobals &g
job.SetAlphaChannelRequired(GenerateJob::kAlphaForceOn);
table->Push(NodeValue::kShaderJob, QVariant::fromValue(job), this);
pushed_job = true;
}
}
if (!pushed_job) {
// Re-push whatever value we received
table->Push(texture_meta);
}
}
ShaderCode TransformDistortNode::GetShaderCode(const QString &shader_id) const
@@ -328,7 +333,7 @@ void TransformDistortNode::Hash(const ValueHint &output, QCryptographicHash &has
if (!matrix.isIdentity()) {
// Add fingerprint
HashAddNodeSignature(hash, output);
HashAddNodeSignature(hash);
hash.addData(reinterpret_cast<const char*>(&matrix), sizeof(matrix));
}
}
+2
View File
@@ -98,6 +98,7 @@ void NodeGraph::childEvent(QChildEvent *event)
connect(node, &Node::InputConnected, this, &NodeGraph::InputConnected);
connect(node, &Node::InputDisconnected, this, &NodeGraph::InputDisconnected);
connect(node, &Node::ValueChanged, this, &NodeGraph::ValueChanged);
connect(node, &Node::InputValueHintChanged, this, &NodeGraph::InputValueHintChanged);
emit NodeAdded(node);
emit node->AddedToGraph(this);
@@ -110,6 +111,7 @@ void NodeGraph::childEvent(QChildEvent *event)
disconnect(node, &Node::InputConnected, this, &NodeGraph::InputConnected);
disconnect(node, &Node::InputDisconnected, this, &NodeGraph::InputDisconnected);
disconnect(node, &Node::ValueChanged, this, &NodeGraph::ValueChanged);
disconnect(node, &Node::InputValueHintChanged, this, &NodeGraph::InputValueHintChanged);
emit NodeRemoved(node);
emit node->RemovedFromGraph(this);
+2
View File
@@ -129,6 +129,8 @@ signals:
void ValueChanged(const NodeInput& input);
void InputValueHintChanged(const NodeInput& input);
void NodePositionAdded(Node *node, Node *relative, const QPointF &position);
void NodePositionRemoved(Node *node, Node *relative);
+2
View File
@@ -359,6 +359,8 @@ void MathNodeBase::ValueInternal(Operation operation, Pairing pairing, const QSt
} else {
output->Push(NodeValue::kSampleJob, QVariant::fromValue(job), this);
}
} else {
output->Push(NodeValue::kSampleJob, QVariant::fromValue(job), this);
}
break;
}
+1 -1
View File
@@ -116,7 +116,7 @@ void MergeNode::Hash(const ValueHint &output, QCryptographicHash &hash, const No
if (!passthrough_base && !passthrough_blend) {
// This merge will actually do something so we add a fingerprint
HashAddNodeSignature(hash, output);
HashAddNodeSignature(hash);
}
if (!passthrough_base) {
+94
View File
@@ -139,6 +139,28 @@ void Node::Load(QXmlStreamReader *reader, XMLNodeData& xml_node_data, uint versi
reader->skipCurrentElement();
}
}
} else if (reader->name() == QStringLiteral("hints")) {
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("hint")) {
QString input;
int element;
XMLAttributeLoop(reader, attr) {
if (attr.name() == QStringLiteral("input")) {
input = attr.value().toString();
} else if (attr.name() == QStringLiteral("element")) {
element = attr.value().toInt();
}
}
ValueHint vh;
vh.Load(reader);
value_hints_.insert({input, element}, vh);
} else {
reader->skipCurrentElement();
}
}
} else {
reader->skipCurrentElement();
}
@@ -179,6 +201,19 @@ void Node::Save(QXmlStreamWriter *writer) const
}
writer->writeEndElement(); // connections
writer->writeStartElement(QStringLiteral("hints"));
for (auto it=value_hints_.cbegin(); it!=value_hints_.cend(); it++) {
writer->writeStartElement(QStringLiteral("hint"));
writer->writeAttribute(QStringLiteral("input"), it.key().input);
writer->writeAttribute(QStringLiteral("element"), QString::number(it.key().element));
it.value().Save(writer);
writer->writeEndElement(); // hint
}
writer->writeEndElement();
writer->writeStartElement(QStringLiteral("custom"));
SaveCustom(writer);
writer->writeEndElement(); // custom
@@ -980,6 +1015,15 @@ int Node::InputArraySize(const QString &id) const
}
}
void Node::SetValueHintForInput(const QString &input, int element, const ValueHint &hint)
{
value_hints_.insert({input, element}, hint);
emit InputValueHintChanged(NodeInput(this, input, element));
InvalidateAll(input, element);
}
const NodeKeyframeTrack &Node::GetTrackFromKeyframe(NodeKeyframe *key) const
{
return GetImmediate(key->input(), key->element())->keyframe_tracks().at(key->track());
@@ -1256,6 +1300,7 @@ bool Node::AreLinked(Node *a, Node *b)
void Node::HashAddNodeSignature(QCryptographicHash &hash) const
{
// Add node ID
hash.addData(id().toUtf8());
}
@@ -1529,6 +1574,9 @@ void Node::CopyValuesOfElement(const Node *src, Node *dst, const QString &input,
if (src_element == -1 && dst_element == -1) {
dst->ArrayResizeInternal(input, src->InputArraySize(input));
}
// Copy value hint
dst->SetValueHintForInput(input, dst_element, src->GetValueHintForInput(input, src_element));
}
bool Node::CanBeDeleted() const
@@ -2408,4 +2456,50 @@ void NodeRemovePositionFromAllContextsCommand::undo()
}
}
void Node::ValueHint::Hash(QCryptographicHash &hash) const
{
// Add value hint
foreach (NodeValue::Type t, type) {
hash.addData(reinterpret_cast<const char*>(&t), sizeof(t));
}
hash.addData(reinterpret_cast<const char *>(&index), sizeof(index));
hash.addData(tag.toUtf8());
}
void Node::ValueHint::Load(QXmlStreamReader *reader)
{
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("types")) {
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("type")) {
type.append(static_cast<NodeValue::Type>(reader->readElementText().toInt()));
} else {
reader->skipCurrentElement();
}
}
} else if (reader->name() == QStringLiteral("index")) {
index = reader->readElementText().toInt();
} else if (reader->name() == QStringLiteral("tag")) {
tag = reader->readElementText();
} else {
reader->skipCurrentElement();
}
}
}
void Node::ValueHint::Save(QXmlStreamWriter *writer) const
{
writer->writeStartElement(QStringLiteral("types"));
for (auto it=type.cbegin(); it!=type.cend(); it++) {
writer->writeTextElement(QStringLiteral("type"), QString::number(*it));
}
writer->writeEndElement(); // types
writer->writeTextElement(QStringLiteral("index"), QString::number(index));
writer->writeTextElement(QStringLiteral("tag"), tag);
}
}
+10 -4
View File
@@ -466,6 +466,11 @@ public:
QVector<NodeValue::Type> type;
int index = -1;
QString tag;
void Hash(QCryptographicHash &hash) const;
void Load(QXmlStreamReader *reader);
void Save(QXmlStreamWriter *writer) const;
};
ValueHint GetValueHintForInput(const QString &input, int element) const
@@ -473,10 +478,7 @@ public:
return value_hints_.value({input, element});
}
void SetValueHintForInput(const QString &input, int element, const ValueHint &hint)
{
value_hints_.insert({input, element}, hint);
}
void SetValueHintForInput(const QString &input, int element, const ValueHint &hint);
const NodeKeyframeTrack& GetTrackFromKeyframe(NodeKeyframe* key) const;
@@ -965,6 +967,8 @@ signals:
void OutputDisconnected(Node *output, const NodeInput& input);
void InputValueHintChanged(const NodeInput& input);
void InputPropertyChanged(const QString& input, const QString& key, const QVariant& value);
void LinksChanged();
@@ -1519,4 +1523,6 @@ private:
}
Q_DECLARE_METATYPE(olive::Node::ValueHint);
#endif // NODE_H
+1
View File
@@ -221,6 +221,7 @@ uint qHash(const NodeKeyframeTrackReference& i);
}
Q_DECLARE_METATYPE(olive::NodeInput)
Q_DECLARE_METATYPE(olive::NodeKeyframeTrackReference)
#endif // NODEPARAM_H
+19 -7
View File
@@ -48,7 +48,8 @@ NodeValueRow NodeTraverser::GenerateRow(NodeValueDatabase *database, const Node
NodeValueRow row;
for (auto it=database->begin(); it!=database->end(); it++) {
// Get hint for which value should be pulled
row.insert(it.key(), GenerateRowValue(node, it.key(), &it.value()));
NodeValue value = GenerateRowValue(node, it.key(), &it.value());
row.insert(it.key(), value);
}
return row;
@@ -82,6 +83,17 @@ NodeValue NodeTraverser::GenerateRowValue(const Node *node, const QString &input
}
NodeValue NodeTraverser::GenerateRowValueElement(const Node *node, const QString &input, int element, NodeValueTable *table)
{
int value_index = GenerateRowValueElementIndex(node, input, element, table);
if (value_index == -1) {
return NodeValue();
} else {
return table->TakeAt(value_index);
}
}
int NodeTraverser::GenerateRowValueElementIndex(const Node *node, const QString &input, int element, const NodeValueTable *table)
{
Node::ValueHint hint = node->GetValueHintForInput(input, element);
QVector<NodeValue::Type> types = hint.type;
@@ -92,23 +104,23 @@ NodeValue NodeTraverser::GenerateRowValueElement(const Node *node, const QString
if (hint.index == -1) {
// Get most recent value with this type and tag
return table->TakeWithMeta(types, hint.tag);
return table->GetValueIndex(types, hint.tag);
} else {
// Try to find value at this index
int index = table->Count() - hint.index;
int index = table->Count() - 1 - hint.index;
int diff = 0;
while (index + diff < table->Count() && index - diff >= 0) {
if (index + diff < table->Count() && types.contains(table->at(index + diff).type())) {
return table->TakeAt(index + diff);
return index + diff;
}
if (index - diff >= 0 && types.contains(table->at(index - diff).type())) {
return table->TakeAt(index - diff);
return index - diff;
}
diff++;
}
return NodeValue();
return -1;
}
}
@@ -270,7 +282,7 @@ QVariant NodeTraverser::ProcessSamples(const Node *node, const TimeRange &range,
Q_UNUSED(range)
Q_UNUSED(job)
return QVariant();
return QVariant::fromValue(SampleBuffer::Create());
}
QVariant NodeTraverser::ProcessFrameGeneration(const Node *node, const GenerateJob &job)
+1
View File
@@ -45,6 +45,7 @@ public:
NodeValue GenerateRowValue(const Node *node, const QString &input, NodeValueTable *table);
NodeValue GenerateRowValueElement(const Node *node, const QString &input, int element, NodeValueTable *table);
int GenerateRowValueElementIndex(const Node *node, const QString &input, int element, const NodeValueTable *table);
static NodeGlobals GenerateGlobals(const VideoParams &params, const TimeRange &time);
static NodeGlobals GenerateGlobals(const VideoParams &params, const rational &time)
+3 -3
View File
@@ -326,7 +326,7 @@ QString NodeValue::GetPrettyDataTypeName(Type type)
NodeValue NodeValueTable::GetWithMeta(const QVector<NodeValue::Type> &type, const QString &tag) const
{
int value_index = GetInternal(type, tag);
int value_index = GetValueIndex(type, tag);
if (value_index >= 0) {
return values_.at(value_index);
@@ -337,7 +337,7 @@ NodeValue NodeValueTable::GetWithMeta(const QVector<NodeValue::Type> &type, cons
NodeValue NodeValueTable::TakeWithMeta(const QVector<NodeValue::Type> &type, const QString &tag)
{
int value_index = GetInternal(type, tag);
int value_index = GetValueIndex(type, tag);
if (value_index >= 0) {
return values_.takeAt(value_index);
@@ -407,7 +407,7 @@ NodeValueTable NodeValueTable::Merge(QList<NodeValueTable> tables)
return merged_table;
}
int NodeValueTable::GetInternal(const QVector<NodeValue::Type>& types, const QString &tag) const
int NodeValueTable::GetValueIndex(const QVector<NodeValue::Type>& types, const QString &tag) const
{
int index = -1;
+2 -2
View File
@@ -391,11 +391,11 @@ public:
return values_.isEmpty();
}
int GetValueIndex(const QVector<NodeValue::Type> &type, const QString& tag) const;
static NodeValueTable Merge(QList<NodeValueTable> tables);
private:
int GetInternal(const QVector<NodeValue::Type> &type, const QString& tag) const;
QVector<NodeValue> values_;
};
+5
View File
@@ -52,6 +52,11 @@ public:
node_view_->ClearGraph();
}
const QVector<Node*> &GetCurrentContexts() const
{
return node_view_->GetCurrentContexts();
}
virtual void SelectAll() override
{
node_view_->SelectAll();
+27 -9
View File
@@ -14,7 +14,6 @@ namespace olive {
PreviewAutoCacher::PreviewAutoCacher() :
viewer_node_(nullptr),
paused_(false),
has_changed_(false),
use_custom_range_(false),
single_frame_render_(nullptr)
{
@@ -141,7 +140,9 @@ void PreviewAutoCacher::HashesProcessed()
// The cacher might be waiting for this job to finish
if (!graph_update_queue_.isEmpty()) {
TryRender();
} else if (hash_iterator_.HasNext()) {
}
if (hash_iterator_.HasNext()) {
// Launch next hashes
QueueNextHashTask();
}
@@ -203,7 +204,12 @@ void PreviewAutoCacher::AudioRendered()
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() - waveform_info.range.in(), r.length());
if (waveform_info.silence) {
block->waveform().OverwriteSilence(r.in(), r.length());
} else {
block->waveform().OverwriteSums(waveform_info.waveform, r.in(), r.in() - waveform_info.range.in(), r.length());
}
}
emit block->PreviewChanged();
@@ -316,6 +322,9 @@ void PreviewAutoCacher::ProcessUpdateQueue()
case QueuedJob::kValueChanged:
CopyValue(job.input);
break;
case QueuedJob::kValueHintChanged:
CopyValueHint(job.input);
break;
}
}
graph_update_queue_.clear();
@@ -379,6 +388,13 @@ void PreviewAutoCacher::CopyValue(const NodeInput &input)
Node::CopyValuesOfElement(input.node(), our_input, input.input(), input.element());
}
void PreviewAutoCacher::CopyValueHint(const NodeInput &input)
{
Node* our_input = copy_map_.value(input.node());
Node::ValueHint hint = input.node()->GetValueHintForInput(input.input(), input.element());
our_input->SetValueHintForInput(input.input(), input.element(), hint);
}
void PreviewAutoCacher::InsertIntoCopyMap(Node *node, Node *copy)
{
// Insert into map
@@ -412,7 +428,6 @@ void PreviewAutoCacher::SetPlayhead(const rational &playhead)
cache_range_ = TimeRange(playhead - Config::Current()[QStringLiteral("DiskCacheBehind")].value<rational>(),
playhead + Config::Current()[QStringLiteral("DiskCacheAhead")].value<rational>());
has_changed_ = true;
use_custom_range_ = false;
RequeueFrames();
@@ -439,7 +454,6 @@ void PreviewAutoCacher::ClearVideoQueue(bool hard)
{
ClearQueueInternal(video_tasks_, hard, &PreviewAutoCacher::VideoRendered);
has_changed_ = true;
use_custom_range_ = false;
queued_frame_iterator_.reset();
}
@@ -486,6 +500,12 @@ void PreviewAutoCacher::ValueChanged(const NodeInput &input)
UpdateGraphChangeValue();
}
void PreviewAutoCacher::ValueHintChanged(const NodeInput &input)
{
graph_update_queue_.append({QueuedJob::kValueHintChanged, nullptr, input, nullptr});
UpdateGraphChangeValue();
}
void PreviewAutoCacher::TryRender()
{
if (!graph_update_queue_.isEmpty()) {
@@ -564,7 +584,6 @@ void PreviewAutoCacher::RequeueFrames()
if (viewer_node_
&& viewer_node_->video_frame_cache()->HasInvalidatedRanges(viewer_node_->GetVideoLength())
&& hash_tasks_.isEmpty()
&& has_changed_
&& VideoParams::FormatIsFloat(viewer_node_->GetVideoParams().format())
&& (!paused_ || use_custom_range_)) {
TimeRange using_range;
@@ -580,8 +599,6 @@ void PreviewAutoCacher::RequeueFrames()
queued_frame_iterator_ = TimeRangeListFrameIterator(invalidated, viewer_node_->video_frame_cache()->GetTimebase());
QueueNextFrameInRange(RenderManager::GetNumberOfIdealConcurrentJobs());
has_changed_ = false;
}
}
@@ -599,7 +616,6 @@ void PreviewAutoCacher::ConformFinished()
void PreviewAutoCacher::ForceCacheRange(const TimeRange &range)
{
has_changed_ = true;
use_custom_range_ = true;
custom_autocache_range_ = range;
@@ -655,6 +671,7 @@ void PreviewAutoCacher::SetViewerNode(ViewerOutput *viewer_node)
disconnect(graph, &NodeGraph::InputConnected, this, &PreviewAutoCacher::EdgeAdded);
disconnect(graph, &NodeGraph::InputDisconnected, this, &PreviewAutoCacher::EdgeRemoved);
disconnect(graph, &NodeGraph::ValueChanged, this, &PreviewAutoCacher::ValueChanged);
disconnect(graph, &NodeGraph::InputValueHintChanged, this, &PreviewAutoCacher::ValueHintChanged);
// Disconnect signal (will be a no-op if the signal was never connected)
disconnect(viewer_node_->video_frame_cache(),
@@ -705,6 +722,7 @@ void PreviewAutoCacher::SetViewerNode(ViewerOutput *viewer_node)
connect(graph, &NodeGraph::InputConnected, this, &PreviewAutoCacher::EdgeAdded);
connect(graph, &NodeGraph::InputDisconnected, this, &PreviewAutoCacher::EdgeRemoved);
connect(graph, &NodeGraph::ValueChanged, this, &PreviewAutoCacher::ValueChanged);
connect(graph, &NodeGraph::InputValueHintChanged, this, &PreviewAutoCacher::ValueHintChanged);
// Copy invalidated ranges - used to determine which frames need hashing
invalidated_video_ = viewer_node_->video_frame_cache()->GetInvalidatedRanges(viewer_node_->GetVideoLength());
+5 -3
View File
@@ -90,6 +90,7 @@ private:
void AddEdge(Node *output, const NodeInput& input);
void RemoveEdge(Node *output, const NodeInput& input);
void CopyValue(const NodeInput& input);
void CopyValueHint(const NodeInput& input);
void InsertIntoCopyMap(Node* node, Node* copy);
@@ -124,7 +125,8 @@ private:
kNodeRemoved,
kEdgeAdded,
kEdgeRemoved,
kValueChanged
kValueChanged,
kValueHintChanged
};
Type type;
@@ -147,8 +149,6 @@ private:
TimeRange cache_range_;
bool has_changed_;
bool use_custom_range_;
TimeRange custom_autocache_range_;
@@ -220,6 +220,8 @@ private slots:
void ValueChanged(const NodeInput& input);
void ValueHintChanged(const NodeInput &input);
/**
* @brief Generic function called whenever the frames to render need to be (re)queued
*/
+41 -37
View File
@@ -176,7 +176,7 @@ void RenderProcessor::Run()
QVariant sample_variant = table.Get(NodeValue::kSamples);
SampleBufferPtr samples = sample_variant.value<SampleBufferPtr>();
if (samples && ticket_->property("enablewaveforms").toBool()) {
if (ticket_->property("enablewaveforms").toBool()) {
AudioVisualWaveform vis;
vis.set_channel_count(samples->audio_params().channel_count());
vis.OverwriteSamples(samples, samples->audio_params().sample_rate());
@@ -264,52 +264,56 @@ NodeValueTable RenderProcessor::GenerateBlockTable(const Track *track, const Tim
// Destination buffer
NodeValueTable table = GenerateTable(b, track->GetValueHintForInput(Track::kBlockInput, track->GetArrayIndexFromBlock(b)),Track::TransformRangeForBlock(b, range_for_block));
SampleBufferPtr samples_from_this_block = table.Take(NodeValue::kSamples).value<SampleBufferPtr>();
ClipBlock *clip_cast = dynamic_cast<ClipBlock*>(b);
if (!samples_from_this_block) {
// If we retrieved no samples from this block, do nothing
continue;
if (samples_from_this_block) {
// If this is a clip, we might have extra speed/reverse information
if (clip_cast) {
double speed_value = clip_cast->speed();
bool reversed = clip_cast->reverse();
if (qIsNull(speed_value)) {
// Just silence, don't think there's any other practical application of 0 speed audio
samples_from_this_block->fill(0);
} else if (!qFuzzyCompare(speed_value, 1.0)) {
// Multiply time
samples_from_this_block->speed(speed_value);
}
if (reversed) {
samples_from_this_block->reverse();
}
}
int copy_length = qMin(max_dest_sz, samples_from_this_block->sample_count());
// Copy samples into destination buffer
for (int i=0; i<samples_from_this_block->audio_params().channel_count(); i++) {
block_range_buffer->set(i, samples_from_this_block->data(i), destination_offset, copy_length);
}
NodeValueTable::Merge({merged_table, table});
}
// If this is a clip, we might have extra speed/reverse information
if (ClipBlock *clip_cast = dynamic_cast<ClipBlock*>(b)) {
double speed_value = clip_cast->speed();
bool reversed = clip_cast->reverse();
// Create block waveforms if requested
if (ticket_->property("enablewaveforms").toBool() && clip_cast) {
// Format information for use in the main thread
RenderedWaveform waveform_info;
waveform_info.block = clip_cast;
waveform_info.range = range_for_block - b->in();
if (qIsNull(speed_value)) {
// Just silence, don't think there's any other practical application of 0 speed audio
samples_from_this_block->fill(0);
} else if (!qFuzzyCompare(speed_value, 1.0)) {
// Multiply time
samples_from_this_block->speed(speed_value);
}
if (reversed) {
samples_from_this_block->reverse();
}
// Create block waveforms if requested
if (ticket_->property("enablewaveforms").toBool()) {
if (!(waveform_info.silence = !samples_from_this_block.get())) {
// 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<RenderedWaveform> waveform_list = ticket_->property("waveforms").value< QVector<RenderedWaveform> >();
waveform_list.append(waveform_info);
ticket_->setProperty("waveforms", QVariant::fromValue(waveform_list));
waveform_info.waveform = visual_waveform;
}
QVector<RenderedWaveform> waveform_list = ticket_->property("waveforms").value< QVector<RenderedWaveform> >();
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());
// Copy samples into destination buffer
for (int i=0; i<samples_from_this_block->audio_params().channel_count(); i++) {
block_range_buffer->set(i, samples_from_this_block->data(i), destination_offset, copy_length);
}
NodeValueTable::Merge({merged_table, table});
}
}
+1
View File
@@ -39,6 +39,7 @@ public:
const ClipBlock* block;
AudioVisualWaveform waveform;
TimeRange range;
bool silence;
};
protected:
+1
View File
@@ -33,6 +33,7 @@ add_subdirectory(nodecombobox)
add_subdirectory(nodeparamview)
add_subdirectory(nodetableview)
add_subdirectory(nodetreeview)
add_subdirectory(nodevaluetree)
add_subdirectory(nodeview)
add_subdirectory(panel)
add_subdirectory(path)
@@ -25,6 +25,7 @@
#include "common/qtutils.h"
#include "core.h"
#include "node/node.h"
#include "widget/collapsebutton/collapsebutton.h"
#include "widget/menu/menu.h"
#include "widget/nodeview/nodeviewundo.h"
@@ -35,20 +36,29 @@ NodeParamViewConnectedLabel::NodeParamViewConnectedLabel(const NodeInput &input,
input_(input),
connected_node_(nullptr)
{
QHBoxLayout* layout = new QHBoxLayout(this);
layout->setSpacing(QtUtils::QFontMetricsWidth(fontMetrics(), QStringLiteral(" ")));
QVBoxLayout *layout = new QVBoxLayout(this);
layout->setMargin(0);
layout->addWidget(new QLabel(tr("Connected to")));
// Set up label area
QHBoxLayout *label_layout = new QHBoxLayout();
label_layout->setSpacing(QtUtils::QFontMetricsWidth(fontMetrics(), QStringLiteral(" ")));
label_layout->setMargin(0);
layout->addLayout(label_layout);
CollapseButton *collapse_btn = new CollapseButton();
collapse_btn->setChecked(false);
label_layout->addWidget(collapse_btn);
label_layout->addWidget(new QLabel(tr("Connected to")));
connected_to_lbl_ = new ClickableLabel();
connected_to_lbl_->setCursor(Qt::PointingHandCursor);
connected_to_lbl_->setContextMenuPolicy(Qt::CustomContextMenu);
connect(connected_to_lbl_, &ClickableLabel::MouseClicked, this, &NodeParamViewConnectedLabel::ConnectionClicked);
connect(connected_to_lbl_, &ClickableLabel::customContextMenuRequested, this, &NodeParamViewConnectedLabel::ShowLabelContextMenu);
layout->addWidget(connected_to_lbl_);
label_layout->addWidget(connected_to_lbl_);
layout->addStretch();
label_layout->addStretch();
// Set up "link" font
QFont link_font = connected_to_lbl_->font();
@@ -64,6 +74,21 @@ NodeParamViewConnectedLabel::NodeParamViewConnectedLabel(const NodeInput &input,
connect(input_.node(), &Node::InputConnected, this, &NodeParamViewConnectedLabel::InputConnected);
connect(input_.node(), &Node::InputDisconnected, this, &NodeParamViewConnectedLabel::InputDisconnected);
// Set up table area
value_tree_ = new NodeValueTree();
value_tree_->setVisible(false);
layout->addWidget(value_tree_);
connect(collapse_btn, &CollapseButton::toggled, this, &NodeParamViewConnectedLabel::SetValueTreeVisible);
}
void NodeParamViewConnectedLabel::SetTime(const rational &time)
{
time_ = time;
if (value_tree_->isVisible()) {
UpdateValueTree();
}
}
void NodeParamViewConnectedLabel::InputConnected(Node *output, const NodeInput& input)
@@ -122,4 +147,18 @@ void NodeParamViewConnectedLabel::UpdateLabel()
connected_to_lbl_->setText(s);
}
void NodeParamViewConnectedLabel::UpdateValueTree()
{
value_tree_->SetNode(input_, time_);
}
void NodeParamViewConnectedLabel::SetValueTreeVisible(bool e)
{
value_tree_->setVisible(e);
if (e) {
UpdateValueTree();
}
}
}
@@ -23,6 +23,7 @@
#include "node/param.h"
#include "widget/clickablelabel/clickablelabel.h"
#include "widget/nodevaluetree/nodevaluetree.h"
namespace olive {
@@ -31,6 +32,8 @@ class NodeParamViewConnectedLabel : public QWidget {
public:
NodeParamViewConnectedLabel(const NodeInput& input, QWidget* parent = nullptr);
void SetTime(const rational &time);
signals:
void RequestSelectNode(const QVector<Node*>& node);
@@ -46,12 +49,21 @@ private slots:
private:
void UpdateLabel();
void UpdateValueTree();
ClickableLabel* connected_to_lbl_;
NodeInput input_;
Node *connected_node_;
NodeValueTree *value_tree_;
rational time_;
private slots:
void SetValueTreeVisible(bool e);
};
}
@@ -383,6 +383,10 @@ void NodeParamViewItemBody::SetTime(const rational &time)
ui_obj.key_control->SetTime(time);
}
if (ui_obj.connected_label) {
ui_obj.connected_label->SetTime(time);
}
ui_obj.widget_bridge->SetTime(time);
}
}
+22
View File
@@ -0,0 +1,22 @@
# Olive - Non-Linear Video Editor
# Copyright (C) 2021 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/>.
set(OLIVE_SOURCES
${OLIVE_SOURCES}
widget/nodevaluetree/nodevaluetree.cpp
widget/nodevaluetree/nodevaluetree.h
PARENT_SCOPE
)
@@ -0,0 +1,83 @@
#include "nodevaluetree.h"
#include <QEvent>
#include "node/traverser.h"
namespace olive {
#define super QTreeWidget
NodeValueTree::NodeValueTree(QWidget *parent) :
super(parent)
{
setColumnWidth(0, 0);
setColumnCount(4);
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
static const int kMinimumRows = 10;
setMinimumHeight(fontMetrics().height() * kMinimumRows);
Retranslate();
}
void NodeValueTree::SetNode(const NodeInput &input, const rational &time)
{
clear();
NodeTraverser traverser;
Node *connected_node = input.GetConnectedOutput();
Node::ValueHint value_hint = input.node()->GetValueHintForInput(input.input(), input.element());
NodeValueTable table = traverser.GenerateTable(connected_node, value_hint, TimeRange(time, time));
int index = traverser.GenerateRowValueElementIndex(input.node(), input.input(), input.element(), &table);
for (int i=0; i<table.Count(); i++) {
const NodeValue &value = table.at(i);
QTreeWidgetItem *item = new QTreeWidgetItem(this);
Node::ValueHint hint = {{value.type()}, table.Count()-1-i, value.tag()};
QRadioButton *radio = new QRadioButton(this);
radio->setProperty("input", QVariant::fromValue(input));
radio->setProperty("hint", QVariant::fromValue(hint));
if (i == index) {
radio->setChecked(true);
}
connect(radio, &QRadioButton::clicked, this, &NodeValueTree::RadioButtonChecked);
setItemWidget(item, 0, radio);
item->setText(1, NodeValue::GetPrettyDataTypeName(value.type()));
item->setText(2, NodeValue::ValueToString(value.type(), value.data(), false));
item->setText(3, value.source()->GetLabelAndName());
}
}
void NodeValueTree::changeEvent(QEvent *event)
{
if (event->type() == QEvent::LanguageChange) {
Retranslate();
}
super::changeEvent(event);
}
void NodeValueTree::Retranslate()
{
setHeaderLabels({QString(), tr("Type"), tr("Value"), tr("Source")});
}
void NodeValueTree::RadioButtonChecked(bool e)
{
if (e) {
QRadioButton *btn = static_cast<QRadioButton*>(sender());
Node::ValueHint hint = btn->property("hint").value<Node::ValueHint>();
NodeInput input = btn->property("input").value<NodeInput>();
input.node()->SetValueHintForInput(input.input(), input.element(), hint);
}
}
}
+32
View File
@@ -0,0 +1,32 @@
#ifndef NODEVALUETREE_H
#define NODEVALUETREE_H
#include <QRadioButton>
#include <QTreeWidget>
#include "node/node.h"
namespace olive {
class NodeValueTree : public QTreeWidget
{
Q_OBJECT
public:
NodeValueTree(QWidget *parent = nullptr);
void SetNode(const NodeInput &input, const rational &time);
protected:
virtual void changeEvent(QEvent *event) override;
private:
void Retranslate();
private slots:
void RadioButtonChecked(bool e);
};
}
#endif // NODEVALUETREE_H
+5
View File
@@ -79,6 +79,11 @@ public:
void ZoomOut();
const QVector<Node*> &GetCurrentContexts() const
{
return filter_nodes_;
}
public slots:
void SetMiniMapEnabled(bool e)
{
+2 -1
View File
@@ -741,8 +741,9 @@ void MainWindow::UpdateNodePanelContextFromTimelinePanel(TimelinePanel *panel)
context.append(viewer);
}
QVector<Node*> old_contexts = node_panel_->GetCurrentContexts();
node_panel_->SetGraph(viewer ? viewer->parent() : nullptr, context);
if (viewer) {
if (viewer && context != old_contexts) {
node_panel_->SelectWithDependencies(context, false);
}
}