From 7ad2eae7a216296a113d8d2a70e3bdc953ba02a3 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Tue, 24 Dec 2019 12:34:30 +1100 Subject: [PATCH] implemented volume and pan effects This commit implements a structure for sample processing and two implemenations of this structure in volume and pan effects. --- app/node/CMakeLists.txt | 1 + app/node/audio/CMakeLists.txt | 23 ++++++++++ app/node/audio/pan/CMakeLists.txt | 22 +++++++++ app/node/audio/pan/pan.cpp | 67 ++++++++++++++++++++++++++++ app/node/audio/pan/pan.h | 27 +++++++++++ app/node/audio/volume/CMakeLists.txt | 22 +++++++++ app/node/audio/volume/volume.cpp | 52 +++++++++++++++++++++ app/node/audio/volume/volume.h | 27 +++++++++++ app/node/factory.cpp | 6 +++ app/node/factory.h | 2 + app/node/node.cpp | 14 ++++++ app/node/node.h | 11 +++++ app/render/backend/exporter.cpp | 2 +- 13 files changed, 275 insertions(+), 1 deletion(-) create mode 100644 app/node/audio/CMakeLists.txt create mode 100644 app/node/audio/pan/CMakeLists.txt create mode 100644 app/node/audio/pan/pan.cpp create mode 100644 app/node/audio/pan/pan.h create mode 100644 app/node/audio/volume/CMakeLists.txt create mode 100644 app/node/audio/volume/volume.cpp create mode 100644 app/node/audio/volume/volume.h diff --git a/app/node/CMakeLists.txt b/app/node/CMakeLists.txt index 85e367ba1..68bb5194f 100644 --- a/app/node/CMakeLists.txt +++ b/app/node/CMakeLists.txt @@ -14,6 +14,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . +add_subdirectory(audio) add_subdirectory(block) add_subdirectory(distort) add_subdirectory(input) diff --git a/app/node/audio/CMakeLists.txt b/app/node/audio/CMakeLists.txt new file mode 100644 index 000000000..e8bfe8647 --- /dev/null +++ b/app/node/audio/CMakeLists.txt @@ -0,0 +1,23 @@ +# Olive - Non-Linear Video Editor +# Copyright (C) 2019 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 . + +add_subdirectory(pan) +add_subdirectory(volume) + +set(OLIVE_SOURCES + ${OLIVE_SOURCES} + PARENT_SCOPE +) diff --git a/app/node/audio/pan/CMakeLists.txt b/app/node/audio/pan/CMakeLists.txt new file mode 100644 index 000000000..66384293d --- /dev/null +++ b/app/node/audio/pan/CMakeLists.txt @@ -0,0 +1,22 @@ +# Olive - Non-Linear Video Editor +# Copyright (C) 2019 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 . + +set(OLIVE_SOURCES + ${OLIVE_SOURCES} + node/audio/pan/pan.h + node/audio/pan/pan.cpp + PARENT_SCOPE +) diff --git a/app/node/audio/pan/pan.cpp b/app/node/audio/pan/pan.cpp new file mode 100644 index 000000000..a289eb8c8 --- /dev/null +++ b/app/node/audio/pan/pan.cpp @@ -0,0 +1,67 @@ +#include "pan.h" + +PanNode::PanNode() +{ + samples_input_ = new NodeInput("samples_in"); + samples_input_->set_data_type(NodeParam::kSamples); + AddInput(samples_input_); + + panning_input_ = new NodeInput("panning_in"); + panning_input_->set_data_type(NodeParam::kFloat); + panning_input_->set_minimum(-1); + panning_input_->set_maximum(1); + panning_input_->set_value_at_time(0, 0); + AddInput(panning_input_); +} + +Node *PanNode::copy() const +{ + return new PanNode(); +} + +QString PanNode::Name() const +{ + return tr("Pan"); +} + +QString PanNode::id() const +{ + return "org.olivevideoeditor.Olive.pan"; +} + +QString PanNode::Category() const +{ + return tr("Audio"); +} + +QString PanNode::Description() const +{ + return tr("Adjust the stereo panning of an audio source."); +} + +bool PanNode::ProcessesSamples() const +{ + return true; +} + +void PanNode::ProcessSamples(const NodeValueDatabase &values, const AudioRenderingParams ¶ms, const float *input, float *output, int index) const +{ + if (params.channel_count() != 2) { + // This node currently only works for stereo audio + return; + } + + float pan_val = values[panning_input_].Get(NodeParam::kFloat).toFloat(); + + if (index%2 == 0) { + // Sample is left channel + if (pan_val > 0) { + output[index] = input[index] * (1.0F - pan_val); + } + } else { + // Sample is right channel + if (pan_val < 0) { + output[index] = input[index] * (1.0F - qAbs(pan_val)); + } + } +} diff --git a/app/node/audio/pan/pan.h b/app/node/audio/pan/pan.h new file mode 100644 index 000000000..a999cdf16 --- /dev/null +++ b/app/node/audio/pan/pan.h @@ -0,0 +1,27 @@ +#ifndef PANNODE_H +#define PANNODE_H + +#include "node/node.h" + +class PanNode : public Node +{ +public: + PanNode(); + + virtual Node* copy() const override; + + virtual QString Name() const override; + virtual QString id() const override; + virtual QString Category() const override; + virtual QString Description() const override; + + virtual bool ProcessesSamples() const override; + virtual void ProcessSamples(const NodeValueDatabase& values, const AudioRenderingParams& params, const float* input, float* output, int index) const override; + +private: + NodeInput* samples_input_; + NodeInput* panning_input_; + +}; + +#endif // PANNODE_H diff --git a/app/node/audio/volume/CMakeLists.txt b/app/node/audio/volume/CMakeLists.txt new file mode 100644 index 000000000..6956ebd0c --- /dev/null +++ b/app/node/audio/volume/CMakeLists.txt @@ -0,0 +1,22 @@ +# Olive - Non-Linear Video Editor +# Copyright (C) 2019 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 . + +set(OLIVE_SOURCES + ${OLIVE_SOURCES} + node/audio/volume/volume.h + node/audio/volume/volume.cpp + PARENT_SCOPE +) diff --git a/app/node/audio/volume/volume.cpp b/app/node/audio/volume/volume.cpp new file mode 100644 index 000000000..87c4c23f3 --- /dev/null +++ b/app/node/audio/volume/volume.cpp @@ -0,0 +1,52 @@ +#include "volume.h" + +VolumeNode::VolumeNode() +{ + samples_input_ = new NodeInput("samples_in"); + samples_input_->set_data_type(NodeParam::kSamples); + AddInput(samples_input_); + + volume_input_ = new NodeInput("volume_in"); + volume_input_->set_data_type(NodeParam::kFloat); + volume_input_->set_minimum(0); + volume_input_->set_maximum(1); + volume_input_->set_value_at_time(0, 1); + AddInput(volume_input_); +} + +Node *VolumeNode::copy() const +{ + return new VolumeNode(); +} + +QString VolumeNode::Name() const +{ + return tr("Volume"); +} + +QString VolumeNode::id() const +{ + return "org.olivevideoeditor.Olive.volume"; +} + +QString VolumeNode::Category() const +{ + return tr("Audio"); +} + +QString VolumeNode::Description() const +{ + return tr("Adjusts the volume of an audio source."); +} + +bool VolumeNode::ProcessesSamples() const +{ + return true; +} + +void VolumeNode::ProcessSamples(const NodeValueDatabase& values, const AudioRenderingParams& params, const float* input, float* output, int index) const +{ + float volume_val = values[volume_input_].Get(NodeParam::kFloat).toFloat(); + + output[index] = input[index] * volume_val; +} diff --git a/app/node/audio/volume/volume.h b/app/node/audio/volume/volume.h new file mode 100644 index 000000000..c7c1f2cf6 --- /dev/null +++ b/app/node/audio/volume/volume.h @@ -0,0 +1,27 @@ +#ifndef VOLUMENODE_H +#define VOLUMENODE_H + +#include "node/node.h" + +class VolumeNode : public Node +{ +public: + VolumeNode(); + + virtual Node* copy() const override; + + virtual QString Name() const override; + virtual QString id() const override; + virtual QString Category() const override; + virtual QString Description() const override; + + virtual bool ProcessesSamples() const override; + virtual void ProcessSamples(const NodeValueDatabase& values, const AudioRenderingParams& params, const float* input, float* output, int index) const override; + +private: + NodeInput* samples_input_; + NodeInput* volume_input_; + +}; + +#endif // VOLUMENODE_H diff --git a/app/node/factory.cpp b/app/node/factory.cpp index c7af032fe..e1a808816 100644 --- a/app/node/factory.cpp +++ b/app/node/factory.cpp @@ -1,5 +1,7 @@ #include "factory.h" +#include "audio/pan/pan.h" +#include "audio/volume/volume.h" #include "block/clip/clip.h" #include "block/gap/gap.h" #include "block/transition/externaltransition.h" @@ -131,6 +133,10 @@ Node *NodeFactory::CreateInternal(const NodeFactory::InternalID &id) return new TrackOutput(); case kViewerOutput: return new ViewerOutput(); + case kAudioVolume: + return new VolumeNode(); + case kAudioPanning: + return new PanNode(); case kInternalNodeCount: break; diff --git a/app/node/factory.h b/app/node/factory.h index b2607ed6a..c1fabffa8 100644 --- a/app/node/factory.h +++ b/app/node/factory.h @@ -18,6 +18,8 @@ public: kVideoInput, kTimelineOutput, kTrackOutput, + kAudioVolume, + kAudioPanning, // Count value kInternalNodeCount diff --git a/app/node/node.cpp b/app/node/node.cpp index 10254bda5..1ce66da7b 100644 --- a/app/node/node.cpp +++ b/app/node/node.cpp @@ -390,6 +390,20 @@ NodeInput *Node::AcceleratedCodeIterativeInput() const return nullptr; } +bool Node::ProcessesSamples() const +{ + return false; +} + +void Node::ProcessSamples(const NodeValueDatabase &values, const AudioRenderingParams ¶ms, const float *input, float *output, int index) const +{ + Q_UNUSED(values) + Q_UNUSED(params) + Q_UNUSED(input) + Q_UNUSED(output) + Q_UNUSED(index) +} + NodeParam *Node::GetParameterWithID(const QString &id) const { foreach (NodeParam* param, params_) { diff --git a/app/node/node.h b/app/node/node.h index b5917fc87..b9fa15cde 100644 --- a/app/node/node.h +++ b/app/node/node.h @@ -32,6 +32,7 @@ #include "node/inputarray.h" #include "node/output.h" #include "node/value.h" +#include "render/audioparams.h" /** * @brief A single processing unit that can be connected with others to create intricate processing systems @@ -156,6 +157,16 @@ public: */ virtual NodeInput* AcceleratedCodeIterativeInput() const; + /** + * @brief Return whether this node processes samples or not + */ + virtual bool ProcessesSamples() const; + + /** + * @brief If ProcessesSamples() is true, this is the function that will process them. + */ + virtual void ProcessSamples(const NodeValueDatabase& values, const AudioRenderingParams& params, const float* input, float* output, int index) const; + /** * @brief Returns the parameter with the specified ID (or nullptr if it doesn't exist) */ diff --git a/app/render/backend/exporter.cpp b/app/render/backend/exporter.cpp index 4b07e2fc4..0d34f1783 100644 --- a/app/render/backend/exporter.cpp +++ b/app/render/backend/exporter.cpp @@ -145,7 +145,7 @@ void Exporter::FrameRendered(const rational &time, QVariant value) } while (cached_frames_.contains(waiting_for_frame_)); if (waiting_for_frame_ >= viewer_node_->Length()) { -S ExportSucceeded(); + ExportSucceeded(); } } else { cached_frames_.insert(time, value);