From a15566fe352e9df979c6fd6f1aeb69869ca39ae7 Mon Sep 17 00:00:00 2001 From: lnd3 Date: Wed, 4 Sep 2024 14:11:47 +0200 Subject: [PATCH] Add more node types for audio processing. --- packages/math/include/math/MathFunc.h | 2 - .../include/nodegraph/NodeGraphOperations.h | 137 +++++++++++++-- .../include/nodegraph/NodeGraphSchema.h | 9 +- .../source/common/NodeGraphOperations.cpp | 157 +++++++++++++++++- .../source/common/NodeGraphSchema.cpp | 17 +- .../rendering/source/common/ui/UIVisitors.cpp | 2 +- 6 files changed, 301 insertions(+), 23 deletions(-) diff --git a/packages/math/include/math/MathFunc.h b/packages/math/include/math/MathFunc.h index 7dfa9795..448965e6 100644 --- a/packages/math/include/math/MathFunc.h +++ b/packages/math/include/math/MathFunc.h @@ -51,7 +51,6 @@ namespace l::math::functions { return llabs(val); } } - return static_cast(0); } @@ -92,7 +91,6 @@ namespace l::math::functions { return ::pow(val, exp); } } - return static_cast(0); } template diff --git a/packages/nodegraph/include/nodegraph/NodeGraphOperations.h b/packages/nodegraph/include/nodegraph/NodeGraphOperations.h index 806da5de..cf930656 100644 --- a/packages/nodegraph/include/nodegraph/NodeGraphOperations.h +++ b/packages/nodegraph/include/nodegraph/NodeGraphOperations.h @@ -66,11 +66,11 @@ namespace l::nodegraph { class GraphSourceSine : public NodeGraphOp { public: GraphSourceSine(NodeGraphBase* node) : - NodeGraphOp(node, 5, 3) + NodeGraphOp(node, 5, 2) {} - std::string defaultInStrings[5] = { "Note", "Vol", "Vib", "Mod", "Reset"}; - std::string defaultOutStrings[3] = { "Sine", "Phase", "Ph. Mod"}; + std::string defaultInStrings[5] = { "Note", "Volume", "Fmod", "Phase", "Reset"}; + std::string defaultOutStrings[2] = { "Sine", "Phase"}; virtual ~GraphSourceSine() = default; @@ -344,19 +344,28 @@ namespace l::nodegraph { /*********************************************************************/ class GraphOutputDebug : public NodeGraphOp { public: - GraphOutputDebug(NodeGraphBase* node, int32_t numValueDisplays) : - NodeGraphOp(node, numValueDisplays, 0, 0) + std::string defaultInStrings[2] = { "Debug", "Smooth" }; + GraphOutputDebug(NodeGraphBase* node) : + NodeGraphOp(node, 1, 0, 2) {} + virtual ~GraphOutputDebug() = default; - bool IsDataVisible(int8_t) override { + virtual void Reset() override; + virtual void Process(std::vector& inputs, std::vector& outputs) override; + virtual bool IsDataVisible(int8_t) override { return true; } - - virtual ~GraphOutputDebug() = default; - - std::string_view GetName() override { + virtual bool IsDataEditable(int8_t channel) override { + return channel == 1; + } + virtual std::string_view GetOutputName(int8_t outputChannel) { + return defaultInStrings[outputChannel]; + } + virtual std::string_view GetName() override { return "Debug"; } + protected: + float mValue = 0.0F; }; /*********************************************************************/ @@ -369,14 +378,13 @@ namespace l::nodegraph { mAudioStream(stream), mCurrentStereoPosition(0) {} + virtual ~GraphOutputSpeaker() = default; + + void Process(std::vector& inputs, std::vector& outputs) override; bool IsDataVisible(int8_t) override { return true; } - - virtual ~GraphOutputSpeaker() = default; - void Process(std::vector& inputs, std::vector& outputs) override; - std::string_view GetOutputName(int8_t outputChannel) { return defaultOutStrings[outputChannel]; } @@ -519,5 +527,106 @@ namespace l::nodegraph { float mLP3 = 0.0f; }; + /*********************************************************************/ + class GraphEffectLimiter : public NodeGraphOp { + public: + std::string defaultInStrings[6] = { "In 1", "In 2", "Attack", "Release", "Preamp", "Limit" }; + std::string defaultOutStrings[3] = { "Out 1", "Out 2", "Envelope"}; + + GraphEffectLimiter(NodeGraphBase* node) : + NodeGraphOp(node, 6, 3, 0) + {} + + virtual ~GraphEffectLimiter() = default; + virtual void Reset() override; + virtual void Process(std::vector& inputs, std::vector& outputs) override; + + virtual bool IsDataVisible(int8_t num) override { + return num >= 2 ? true : false; + } + virtual bool IsDataEditable(int8_t num) override { + return num >= 2 ? true : false; + } + std::string_view GetInputName(int8_t inputChannel) { + return defaultInStrings[inputChannel]; + } + std::string_view GetOutputName(int8_t outputChannel) { + return defaultOutStrings[outputChannel]; + } + std::string_view GetName() override { + return "Limiter"; + } + + protected: + float mEnvelope = 0.0f; + }; + + /*********************************************************************/ + class GraphEffectEnvelopeFollower : public NodeGraphOp { + public: + std::string defaultInStrings[6] = { "In 1", "In 2", "Attack", "Release" }; + std::string defaultOutStrings[3] = { "Envelope" }; + + GraphEffectEnvelopeFollower(NodeGraphBase* node) : + NodeGraphOp(node, 4, 1, 0) + {} + + virtual ~GraphEffectEnvelopeFollower() = default; + virtual void Reset() override; + virtual void Process(std::vector& inputs, std::vector& outputs) override; + + virtual bool IsDataVisible(int8_t num) override { + return num >= 2 ? true : false; + } + virtual bool IsDataEditable(int8_t num) override { + return num >= 2 ? true : false; + } + std::string_view GetInputName(int8_t inputChannel) { + return defaultInStrings[inputChannel]; + } + std::string_view GetOutputName(int8_t outputChannel) { + return defaultOutStrings[outputChannel]; + } + std::string_view GetName() override { + return "Envelope Follower"; + } + + protected: + float mEnvelope = 0.0f; + }; + + /*********************************************************************/ + class GraphEffectSaturator : public NodeGraphOp { + public: + std::string defaultInStrings[6] = { "In 1", "In 2", "Wet", "Preamp", "Limit", "Postamp"}; + std::string defaultOutStrings[3] = { "Out 1", "Out 2"}; + + GraphEffectSaturator(NodeGraphBase* node) : + NodeGraphOp(node, 6, 2, 0) + {} + + virtual ~GraphEffectSaturator() = default; + virtual void Reset() override; + virtual void Process(std::vector& inputs, std::vector& outputs) override; + + virtual bool IsDataVisible(int8_t num) override { + return num >= 2 ? true : false; + } + virtual bool IsDataEditable(int8_t num) override { + return num >= 2 ? true : false; + } + std::string_view GetInputName(int8_t inputChannel) { + return defaultInStrings[inputChannel]; + } + std::string_view GetOutputName(int8_t outputChannel) { + return defaultOutStrings[outputChannel]; + } + std::string_view GetName() override { + return "Saturator"; + } + + protected: + float mEnvelope = 0.0f; + }; } diff --git a/packages/nodegraph/include/nodegraph/NodeGraphSchema.h b/packages/nodegraph/include/nodegraph/NodeGraphSchema.h index 9930d8f7..f7197b1b 100644 --- a/packages/nodegraph/include/nodegraph/NodeGraphSchema.h +++ b/packages/nodegraph/include/nodegraph/NodeGraphSchema.h @@ -44,15 +44,20 @@ namespace l::nodegraph { RegisterNodeType("Numeric", 52, "Negate"); RegisterNodeType("Numeric", 53, "Multiply"); RegisterNodeType("Numeric", 54, "Integral"); + RegisterNodeType("Numeric", 55, "Multiply3"); + RegisterNodeType("Numeric", 56, "Multiply & Add"); RegisterNodeType("Logic", 100, "And"); RegisterNodeType("Logic", 101, "Or"); RegisterNodeType("Logic", 102, "Xor"); - RegisterNodeType("Filter", 150, "Lowpass Filter"); - RegisterNodeType("Output", 200, "Value Debug"); + RegisterNodeType("Filter", 150, "Lowpass"); + RegisterNodeType("Output", 200, "Debug"); RegisterNodeType("Output", 201, "Speaker"); RegisterNodeType("Effect", 250, "Envelope"); RegisterNodeType("Effect", 251, "Reverb1"); RegisterNodeType("Effect", 252, "Reverb2"); + RegisterNodeType("Effect", 253, "Limiter"); + RegisterNodeType("Effect", 254, "Envelope Follower"); + RegisterNodeType("Effect", 255, "Saturator"); } ~NodeGraphSchema() = default; diff --git a/packages/nodegraph/source/common/NodeGraphOperations.cpp b/packages/nodegraph/source/common/NodeGraphOperations.cpp index c7f3f32d..f76e5685 100644 --- a/packages/nodegraph/source/common/NodeGraphOperations.cpp +++ b/packages/nodegraph/source/common/NodeGraphOperations.cpp @@ -139,8 +139,7 @@ namespace l::nodegraph { phaseMod -= floorf(phaseMod); outputs.at(0).mOutput = volume * sinf(3.141529f * (mPhase + phaseMod)); - outputs.at(1).mOutput = mPhase; - outputs.at(2).mOutput = phaseMod; + outputs.at(1).mOutput = 0.5f * phaseMod; } std::string_view GraphSourceSine::GetInputName(int8_t inputChannel) { @@ -345,6 +344,7 @@ namespace l::nodegraph { float attackFrames = inputs.at(1).Get() * 44100.0f / 1000.0f; float releaseFrames = inputs.at(2).Get() * 44100.0f / 1000.0f; float noteFade = inputs.at(3).Get(); + //noteFade = l::math::functions::pow(0.01f, 1.0f / (1000.0f * inputs.at(3).Get() * 44100.0f * 0.001f)); if (noteTarget != 0.0f && mFrameCount < attackFrames) { if (mFrameCount == 0) { @@ -363,6 +363,7 @@ namespace l::nodegraph { mEnvelopeTarget = 1.0f; } mNote += noteFade * noteFade * (noteTarget - mNote); + //mNote = noteFade * (mEnvelope - noteTarget) + noteTarget; } else { // release @@ -384,6 +385,20 @@ namespace l::nodegraph { outputs.at(1).mOutput = mEnvelope * mEnvelope; } + /*********************************************************************/ + void GraphOutputDebug::Reset() { + mValue = 0.0; + mNode->SetInput(1, 0.5f); + mNode->SetInputBound(1, InputBound::INPUT_0_TO_1); + } + + void GraphOutputDebug::Process(std::vector& inputs, std::vector&) { + float value = inputs.at(0).Get(); + float friction = inputs.at(1).Get(); + mValue += friction * friction * (value - mValue); + inputs.at(2).mInput.mInputFloatConstant = mValue; + } + /*********************************************************************/ void GraphOutputSpeaker::Process(std::vector& inputs, std::vector&) { auto& buffer = mAudioStream->GetWriteBuffer(); @@ -456,7 +471,6 @@ namespace l::nodegraph { void GraphEffectReverb2::Reset() { // { "In 1", "In 2", "Mix", "Feedback", "Room Size", "Width", "First tap", "Longest tap", "Num taps", "Tap bulge", "Filter cutoff", "Filter res"}; - mNode->SetInput(2, 0.3f); mNode->SetInput(3, 0.5f); mNode->SetInput(4, 30.0f); @@ -591,10 +605,147 @@ namespace l::nodegraph { //buf1[bufIndex] += centralDelay; // + } + + /*********************************************************************/ + void GraphEffectLimiter::Reset() { + // { "In 1", "In 2", "Attack", "Release", "Preamp", "Limit"}; + mEnvelope = 0.0f; + mNode->SetInput(2, 5.0f); + mNode->SetInput(3, 100.0f); + mNode->SetInput(4, 1.0f); + mNode->SetInput(5, 0.95f); + mNode->SetInputBound(2, InputBound::INPUT_CUSTOM, 1.0f, 10000.0f); + mNode->SetInputBound(3, InputBound::INPUT_CUSTOM, 1.0f, 10000.0f); + mNode->SetInputBound(4, InputBound::INPUT_CUSTOM, 0.0f, 10.0f); + mNode->SetInputBound(5, InputBound::INPUT_CUSTOM, 0.0f, 10.0f); + } + + void GraphEffectLimiter::Process(std::vector& inputs, std::vector& outputs) { + float attackMs = inputs .at(2).Get(); + float releaseMs = inputs.at(3).Get(); + float attack = l::math::functions::pow(0.01f, 1.0f / (attackMs * 44100.0f * 0.001f)); + float release = l::math::functions::pow(0.01f, 1.0f / (releaseMs * 44100.0f * 0.001f)); + float preamp = inputs.at(4).Get(); + float limit = inputs.at(5).Get(); + + float in0 = inputs.at(0).Get(); + float in1 = inputs.at(1).Get(); + + float inVal0 = preamp * in0; + float inVal1 = preamp * in1; + float inVal = 0.5f * (inVal0 + inVal1); + if (inVal > mEnvelope) { + mEnvelope = attack * (mEnvelope - inVal) + inVal; + } + else { + mEnvelope = release * (mEnvelope - inVal) + inVal; + } + + float envelopeAbs = l::math::functions::abs(mEnvelope); + if (envelopeAbs > limit) { + if (envelopeAbs > 1.0f) { + outputs.at(0).mOutput = inVal0 / mEnvelope; + outputs.at(1).mOutput = inVal1 / mEnvelope; + } + else { + outputs.at(0).mOutput = inVal0 / (1.0f + mEnvelope - limit); + outputs.at(1).mOutput = inVal1 / (1.0f + mEnvelope - limit); + } + } + else { + outputs.at(0).mOutput = in0; + outputs.at(1).mOutput = in1; + } + outputs.at(2).mOutput = envelopeAbs; + } + + /*********************************************************************/ + void GraphEffectEnvelopeFollower::Reset() { + mEnvelope = 0.0f; + mNode->SetInput(2, 5.0f); + mNode->SetInput(3, 100.0f); + mNode->SetInputBound(2, InputBound::INPUT_CUSTOM, 1.0f, 10000.0f); + mNode->SetInputBound(3, InputBound::INPUT_CUSTOM, 1.0f, 10000.0f); + } + + void GraphEffectEnvelopeFollower::Process(std::vector& inputs, std::vector& outputs) { + float attackMs = inputs.at(2).Get(); + float releaseMs = inputs.at(3).Get(); + float attack = l::math::functions::pow(0.01f, 1.0f / (attackMs * 44100.0f * 0.001f)); + float release = l::math::functions::pow(0.01f, 1.0f / (releaseMs * 44100.0f * 0.001f)); + float in0 = inputs.at(0).Get(); + float in1 = inputs.at(1).Get(); + + float inVal = 0.5f * (in0 + in1); + if (inVal > mEnvelope) { + mEnvelope = attack * (mEnvelope - inVal) + inVal; + } + else { + mEnvelope = release * (mEnvelope - inVal) + inVal; + } + + float envelopeAbs = l::math::functions::abs(mEnvelope); + outputs.at(0).mOutput = envelopeAbs; } /*********************************************************************/ + void GraphEffectSaturator::Reset() { + // { "In 1", "In 2", "Wet", "Preamp", "Limit", "Postamp"}; + mEnvelope = 0.0f; + mNode->SetInput(2, 0.5f); + mNode->SetInput(3, 1.5f); + mNode->SetInput(4, 0.6f); + mNode->SetInput(5, 1.4f); + mNode->SetInputBound(2, InputBound::INPUT_0_TO_1); + mNode->SetInputBound(3, InputBound::INPUT_CUSTOM, 0.0f, 10.0f); + mNode->SetInputBound(4, InputBound::INPUT_CUSTOM, 0.0f, 10.0f); + mNode->SetInputBound(5, InputBound::INPUT_CUSTOM, 0.0f, 10.0f); + } + + void GraphEffectSaturator::Process(std::vector& inputs, std::vector& outputs) { + float wet = inputs.at(2).Get(); + float preamp = inputs.at(3).Get(); + float limit = inputs.at(4).Get(); + float postamp = inputs.at(5).Get(); + wet = postamp * wet; + float dry = postamp * (1.0f - wet); + float in0 = inputs.at(0).Get(); + float in1 = inputs.at(1).Get(); + + auto sigmoid = [](float x) { + if (x < 1.0f && x > -1.0f) { + return x * (1.5f - 0.5f * x * x); + } + else { + return x > 0 ? 1.0f : -1.0f; + } + }; + + float inPreamp0 = in0 * preamp; + float inPreamp1 = in1 * preamp; + + if (inPreamp0 >= limit || inPreamp0 <= -limit) { + if (inPreamp0 > 0.0f) { + inPreamp0 = limit + (1.0f - limit) * sigmoid((inPreamp0 - limit) / ((1.0f - limit) * 1.5f)); + } + else { + inPreamp0 = -(limit + (1.0f - limit) * sigmoid((-inPreamp0 - limit) / ((1.0f - limit) * 1.5f))); + } + } + if (inPreamp1 >= limit || inPreamp1 <= -limit) { + if (inPreamp1 > 0.0f) { + inPreamp1 = limit + (1.0f - limit) * sigmoid((inPreamp1 - limit) / ((1.0f - limit) * 1.5f)); + } + else { + inPreamp1 = -(limit + (1.0f - limit) * sigmoid((-inPreamp1 - limit) / ((1.0f - limit) * 1.5f))); + } + } + + outputs.at(0).mOutput = dry * in0 + wet * inPreamp0; + outputs.at(1).mOutput = dry * in1 + wet * inPreamp1; + } } diff --git a/packages/nodegraph/source/common/NodeGraphSchema.cpp b/packages/nodegraph/source/common/NodeGraphSchema.cpp index 157e99d0..8816a657 100644 --- a/packages/nodegraph/source/common/NodeGraphSchema.cpp +++ b/packages/nodegraph/source/common/NodeGraphSchema.cpp @@ -57,6 +57,12 @@ namespace l::nodegraph { case 54: node = mMainNodeGraph.NewNode(OutputType::Default); break; + case 55: + node = mMainNodeGraph.NewNode(OutputType::Default); + break; + case 56: + node = mMainNodeGraph.NewNode(OutputType::Default); + break; case 100: node = mMainNodeGraph.NewNode(OutputType::Default); break; @@ -70,7 +76,7 @@ namespace l::nodegraph { node = mMainNodeGraph.NewNode(OutputType::Default); break; case 200: - node = mMainNodeGraph.NewNode(OutputType::ExternalOutput, 1); + node = mMainNodeGraph.NewNode(OutputType::ExternalOutput); break; case 201: node = mMainNodeGraph.NewNode(OutputType::ExternalOutput, mAudioOutput); @@ -84,6 +90,15 @@ namespace l::nodegraph { case 252: node = mMainNodeGraph.NewNode(OutputType::Default); break; + case 253: + node = mMainNodeGraph.NewNode(OutputType::Default); + break; + case 254: + node = mMainNodeGraph.NewNode(OutputType::Default); + break; + case 255: + node = mMainNodeGraph.NewNode(OutputType::Default); + break; default: ASSERT(typeId < 10000) << "Custom node id's begin at id 1000"; ASSERT(mCreateCustomNode != nullptr) << "Custom nodes needs a handler to create them. It's missing."; diff --git a/packages/rendering/source/common/ui/UIVisitors.cpp b/packages/rendering/source/common/ui/UIVisitors.cpp index 00d71bce..dc208bcc 100644 --- a/packages/rendering/source/common/ui/UIVisitors.cpp +++ b/packages/rendering/source/common/ui/UIVisitors.cpp @@ -341,7 +341,7 @@ namespace l::ui { if (mNGSchema) { auto node = mNGSchema->GetNode(container.GetNodeId()); float nodeValue = 0.0f; - if (container.GetChannelId() < node->GetNumInputs()) { + if (container.GetChannelId() < node->GetNumInputs() + node->GetNumConstants()) { nodeValue = node->GetInput(static_cast(container.GetChannelId())); } else {