From c7823c58e9fdc816284f011e3027ef15cea75320 Mon Sep 17 00:00:00 2001 From: lnd3 Date: Sat, 7 Sep 2024 19:17:50 +0200 Subject: [PATCH] Add a trance gate. --- packages/math/include/math/MathFunc.h | 2 +- .../include/nodegraph/NodeGraphOperations.h | 53 ++++++++++++++++ .../include/nodegraph/NodeGraphSchema.h | 1 + .../source/common/NodeGraphOperations.cpp | 62 +++++++++++++++++++ .../source/common/NodeGraphSchema.cpp | 3 + 5 files changed, 120 insertions(+), 1 deletion(-) diff --git a/packages/math/include/math/MathFunc.h b/packages/math/include/math/MathFunc.h index ac628628..7e3e79c2 100644 --- a/packages/math/include/math/MathFunc.h +++ b/packages/math/include/math/MathFunc.h @@ -233,7 +233,7 @@ namespace l::math::functions { } // Gain - // Remapping the unit interval into the unit interval by expanding the sidesand compressing the center, and keeping 1 / 2 mapped to 1 / 2, that can be done with the gain() function.This was a common function in RSL tutorials(the Renderman Shading Language).k = 1 is the identity curve, k < 1 produces the classic gain() shape, and k>1 produces "s" shaped curces.The curves are symmetric(and inverse) for k = a and k = 1 / a. + // Remapping the unit interval into the unit interval by expanding the sides and compressing the center, and keeping 1 / 2 mapped to 1 / 2, that can be done with the gain() function.This was a common function in RSL tutorials(the Renderman Shading Language).k = 1 is the identity curve, k < 1 produces the classic gain() shape, and k>1 produces "s" shaped curces.The curves are symmetric(and inverse) for k = a and k = 1 / a. template T gain(T x, T k) { diff --git a/packages/nodegraph/include/nodegraph/NodeGraphOperations.h b/packages/nodegraph/include/nodegraph/NodeGraphOperations.h index 24afe859..3723058e 100644 --- a/packages/nodegraph/include/nodegraph/NodeGraphOperations.h +++ b/packages/nodegraph/include/nodegraph/NodeGraphOperations.h @@ -835,6 +835,59 @@ namespace l::nodegraph { float mEnvelope = 0.0f; }; + /*********************************************************************/ + class GraphEffectTranceGate : public NodeGraphOp { + public: + std::string defaultInStrings[6] = { "In 1", "In 2", "Bpm", "Fmod", "Attack", "Pattern" }; + std::string defaultOutStrings[3] = { "Out 1", "Out 2" }; + + const std::vector< std::vector> patterns = { + {0.7f, 0.0f, 0.7f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f }, + {0.5f, 0.0f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f}, + {0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f}, + {1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f}, + {0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f}, + {1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, + {1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f}, + {1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f}, + {0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f}, + {1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f }, + }; + + GraphEffectTranceGate(NodeGraphBase* node) : + NodeGraphOp(node, 6, 2, 0) + {} + + virtual ~GraphEffectTranceGate() = default; + virtual void Reset() override; + virtual void Process(int32_t numSamples, 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; + } + virtual std::string_view GetInputName(int8_t inputChannel) override { + return defaultInStrings[inputChannel]; + } + virtual std::string_view GetOutputName(int8_t outputChannel) override { + return defaultOutStrings[outputChannel]; + } + virtual std::string_view GetName() override { + return "Trance Gate"; + } + + protected: + float mSamplesUntilUpdate = 0.0f; + int32_t mGateIndex = 0; + std::vector mGate; + float mGainTarget = 1.0f; + float mGain = 1.0f; + float mGateSmoothing = 0.01f; + float mGateSmoothingNeg = 0.01f; + }; + + /*********************************************************************/ class GraphInputKeyboardPiano : public NodeGraphOp, public l::hid::INoteProcessor { public: diff --git a/packages/nodegraph/include/nodegraph/NodeGraphSchema.h b/packages/nodegraph/include/nodegraph/NodeGraphSchema.h index aa153e48..164eb0ae 100644 --- a/packages/nodegraph/include/nodegraph/NodeGraphSchema.h +++ b/packages/nodegraph/include/nodegraph/NodeGraphSchema.h @@ -64,6 +64,7 @@ namespace l::nodegraph { RegisterNodeType("Effect", 253, "Limiter"); RegisterNodeType("Effect", 254, "Envelope Follower"); RegisterNodeType("Effect", 255, "Saturator"); + RegisterNodeType("Effect", 256, "Trance Gate"); RegisterNodeType("Input", 300, "Keyboard Piano"); RegisterNodeType("Input", 301, "Midi Keyboard"); RegisterNodeType("Input", 302, "Midi Knobs"); diff --git a/packages/nodegraph/source/common/NodeGraphOperations.cpp b/packages/nodegraph/source/common/NodeGraphOperations.cpp index 99be6c6c..b41b0afd 100644 --- a/packages/nodegraph/source/common/NodeGraphOperations.cpp +++ b/packages/nodegraph/source/common/NodeGraphOperations.cpp @@ -872,6 +872,68 @@ namespace l::nodegraph { outputs.at(1).mOutput = dry * in1 + wet * inPreamp1; } + /*********************************************************************/ + void GraphEffectTranceGate::Reset() { + // { "In 1", "In 2", "Bpm", "Fmod", "Attack", "Pattern"}; + + mGateIndex = 0; + + mNode->SetInput(2, 60.0f); + mNode->SetInput(3, 1.0f); + mNode->SetInput(4, 0.001f); + mNode->SetInput(5, 0.0f); + mNode->SetInputBound(2, InputBound::INPUT_CUSTOM, 1.0f, 1000.0f); + mNode->SetInputBound(3, InputBound::INPUT_CUSTOM, 0.01f, 1.0f); + mNode->SetInputBound(4, InputBound::INPUT_0_TO_1); + mNode->SetInputBound(5, InputBound::INPUT_0_100); + } + + void GraphEffectTranceGate::Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) { + // "Bpm", "Fmod", "Attack", "Pattern" + + + float bpm = inputs.at(2).Get(); + //fmod = l::math::functions::pow(l::math::constants::E_f, (1.0f / 10.0f) * fmod); // range 0.1 - 10 with arg -1,1 + + float attack = inputs.at(4).Get(); + + size_t patternsSize = patterns.size(); + int32_t patternId = static_cast(patternsSize * inputs.at(5).Get()); + auto& gate = patterns[patternId % patternsSize]; + + size_t patternSize = gate.size(); + float fmod = inputs.at(3).Get() / static_cast(patternSize); + mGateIndex %= patternSize; + + mGateSmoothing = attack * attack; + mGateSmoothingNeg = mGateSmoothing * 0.25f; + + float freq = 44100.0f * 60.0f / bpm; + + mSamplesUntilUpdate = l::audio::BatchUpdate(freq * fmod, mSamplesUntilUpdate, 0, numSamples, + [&]() { + mGainTarget = gate[mGateIndex]; + mGateIndex = (mGateIndex + 1) % gate.size(); + }, + [&](int32_t start, int32_t end, bool) { + for (int32_t i = start; i < end; i++) { + float in0 = inputs.at(0).Get(); + float in1 = inputs.at(1).Get(); + + float delta = mGainTarget - mGain; + if (delta > 0) { + mGain += mGateSmoothing * delta; + } + else { + mGain += mGateSmoothingNeg * delta; + } + + outputs.at(0).mOutput = mGain * in0; + outputs.at(1).mOutput = mGain * in1; + } + }); + } + /*********************************************************************/ void GraphInputKeyboardPiano::Process(int32_t, std::vector& inputs, std::vector& outputs) { for (size_t i = 0; i < inputs.size(); i++) { diff --git a/packages/nodegraph/source/common/NodeGraphSchema.cpp b/packages/nodegraph/source/common/NodeGraphSchema.cpp index 10e9b250..ec22038b 100644 --- a/packages/nodegraph/source/common/NodeGraphSchema.cpp +++ b/packages/nodegraph/source/common/NodeGraphSchema.cpp @@ -115,6 +115,9 @@ namespace l::nodegraph { case 255: node = mMainNodeGraph.NewNode(OutputType::Default); break; + case 256: + node = mMainNodeGraph.NewNode(OutputType::Default); + break; case 300: node = mMainNodeGraph.NewNode(OutputType::Default, 1, mKeyState); break;