From 3dd35b4b73b215f1f5d1eafe0e9d1888c93e752c Mon Sep 17 00:00:00 2001 From: lnd3 Date: Sat, 7 Sep 2024 02:52:47 +0200 Subject: [PATCH 01/42] Add a midi linux dummy. --- packages/hid/source/linux/MidiLinux.cpp | 21 +++++++++++++++++++++ packages/hid/source/linux/MidiLinux.h | 21 +++++++++++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 packages/hid/source/linux/MidiLinux.cpp create mode 100644 packages/hid/source/linux/MidiLinux.h diff --git a/packages/hid/source/linux/MidiLinux.cpp b/packages/hid/source/linux/MidiLinux.cpp new file mode 100644 index 00000000..d44cb789 --- /dev/null +++ b/packages/hid/source/linux/MidiLinux.cpp @@ -0,0 +1,21 @@ +#include "logging/Log.h" + +#include "MidiLinux.h" + +namespace l::hid::midi { + + std::unique_ptr CreateMidiManager() { + return nullptr; + } + + void MidiManagerLinux::RegisterCallback(CallbackFunction) { + } + + uint32_t MidiManagerLinux::GetNumDevices() { + return 0; + } + + void MidiManagerLinux::SendToDevice(uint32_t, uint32_t, uint32_t, uint32_t, uint32_t) { + } +} + diff --git a/packages/hid/source/linux/MidiLinux.h b/packages/hid/source/linux/MidiLinux.h new file mode 100644 index 00000000..9fd56713 --- /dev/null +++ b/packages/hid/source/linux/MidiLinux.h @@ -0,0 +1,21 @@ +#pragma once + +#include "logging/Log.h" + +#include "hid/Midi.h" + +namespace l::hid::midi { + + class MidiManagerLinux : public MidiManager { + public: + MidiManagerLinux() = default; + + virtual void RegisterCallback(CallbackFunction cb) override; + virtual uint32_t GetNumDevices() override; + virtual void SendToDevice(uint32_t deviceIndex, uint32_t status, uint32_t channel, uint32_t data1, uint32_t data2) override; + protected: + }; + +} + + From 59773b4bf67065a575f544f35c079e42ad2fe019 Mon Sep 17 00:00:00 2001 From: lnd3 Date: Sat, 7 Sep 2024 08:46:04 +0200 Subject: [PATCH 02/42] Add midi buttons nodes. Fix midi unregister function. --- packages/hid/include/hid/Midi.h | 6 +- packages/hid/source/common/Midi.cpp | 21 +++- packages/hid/source/linux/MidiLinux.cpp | 3 - packages/hid/source/linux/MidiLinux.h | 1 - packages/hid/source/windows/MidiWindows.cpp | 11 -- packages/hid/source/windows/MidiWindows.h | 2 - .../nodegraph/include/nodegraph/NodeGraph.h | 12 ++ .../include/nodegraph/NodeGraphOperations.h | 103 ++++++++++++++++++ .../include/nodegraph/NodeGraphSchema.h | 5 + .../nodegraph/source/common/NodeGraph.cpp | 4 + .../source/common/NodeGraphOperations.cpp | 54 ++++++++- .../source/common/NodeGraphSchema.cpp | 15 +++ 12 files changed, 212 insertions(+), 25 deletions(-) diff --git a/packages/hid/include/hid/Midi.h b/packages/hid/include/hid/Midi.h index 9a6e8daa..352d45f6 100644 --- a/packages/hid/include/hid/Midi.h +++ b/packages/hid/include/hid/Midi.h @@ -32,7 +32,8 @@ namespace l::hid::midi { namespace details { extern std::mutex midiCallbackMutex; - extern std::vector midiCallback; + extern int32_t midiGuid; + extern std::unordered_map midiCallback; void HandleMidiData(uint32_t msg, uint32_t instance, uint32_t param1, uint32_t param2); } @@ -46,7 +47,8 @@ namespace l::hid::midi { } virtual ~MidiManager() = default; - virtual void RegisterCallback(CallbackFunction cb); + virtual int32_t RegisterCallback(CallbackFunction cb); + virtual void UnregisterCallback(int32_t id); virtual uint32_t GetNumDevices(); virtual void SendToDevice(uint32_t deviceIndex, uint32_t status, uint32_t channel, uint32_t data1, uint32_t data2); }; diff --git a/packages/hid/source/common/Midi.cpp b/packages/hid/source/common/Midi.cpp index 9b60b598..82d5358c 100644 --- a/packages/hid/source/common/Midi.cpp +++ b/packages/hid/source/common/Midi.cpp @@ -2,11 +2,14 @@ #include "hid/Midi.h" +#include + namespace l::hid::midi { namespace details { std::mutex midiCallbackMutex; - std::vector midiCallback; + int32_t midiGuid = 0; + std::unordered_map midiCallback; void HandleMidiData(uint32_t msg, uint32_t instance, uint32_t param1, uint32_t param2) { switch (msg) { @@ -49,17 +52,25 @@ namespace l::hid::midi { data.unused = 0; for (auto& cb : midiCallback) { - if (cb) { - (cb)(data); + if (cb.second) { + cb.second(data); } } } } } - void MidiManager::RegisterCallback(CallbackFunction) { + int32_t MidiManager::RegisterCallback(CallbackFunction f) { + std::lock_guard lock(details::midiCallbackMutex); + int32_t id = details::midiGuid++; + details::midiCallback.emplace(id, f); + return id; + } + + void MidiManager::UnregisterCallback(int32_t id) { + details::midiCallback.erase(id); } - + uint32_t MidiManager::GetNumDevices() { return 0; } diff --git a/packages/hid/source/linux/MidiLinux.cpp b/packages/hid/source/linux/MidiLinux.cpp index d44cb789..bd629dca 100644 --- a/packages/hid/source/linux/MidiLinux.cpp +++ b/packages/hid/source/linux/MidiLinux.cpp @@ -8,9 +8,6 @@ namespace l::hid::midi { return nullptr; } - void MidiManagerLinux::RegisterCallback(CallbackFunction) { - } - uint32_t MidiManagerLinux::GetNumDevices() { return 0; } diff --git a/packages/hid/source/linux/MidiLinux.h b/packages/hid/source/linux/MidiLinux.h index 9fd56713..5581792c 100644 --- a/packages/hid/source/linux/MidiLinux.h +++ b/packages/hid/source/linux/MidiLinux.h @@ -10,7 +10,6 @@ namespace l::hid::midi { public: MidiManagerLinux() = default; - virtual void RegisterCallback(CallbackFunction cb) override; virtual uint32_t GetNumDevices() override; virtual void SendToDevice(uint32_t deviceIndex, uint32_t status, uint32_t channel, uint32_t data1, uint32_t data2) override; protected: diff --git a/packages/hid/source/windows/MidiWindows.cpp b/packages/hid/source/windows/MidiWindows.cpp index 20a94d0b..dd6126a3 100644 --- a/packages/hid/source/windows/MidiWindows.cpp +++ b/packages/hid/source/windows/MidiWindows.cpp @@ -32,8 +32,6 @@ namespace l::hid::midi { } } - - Midi::Midi() : mHeader{}, mMidiBuffer{} { UINT nMidiDeviceNum = midiInGetNumDevs(); if (nMidiDeviceNum == 0) { @@ -115,11 +113,6 @@ namespace l::hid::midi { } } - void Midi::registerMidiCallback(CallbackFunction f) { - std::lock_guard lock(details::midiCallbackMutex); - details::midiCallback.push_back(f); - } - uint32_t Midi::getNumDevices() { return static_cast(devices.size()); } @@ -230,10 +223,6 @@ namespace l::hid::midi { } } - void MidiManagerWindows::RegisterCallback(CallbackFunction cb) { - mMidiDevice.registerMidiCallback(cb); - } - uint32_t MidiManagerWindows::GetNumDevices() { return mMidiDevice.getNumDevices(); } diff --git a/packages/hid/source/windows/MidiWindows.h b/packages/hid/source/windows/MidiWindows.h index e9815954..99834a3b 100644 --- a/packages/hid/source/windows/MidiWindows.h +++ b/packages/hid/source/windows/MidiWindows.h @@ -20,7 +20,6 @@ namespace l::hid::midi { Midi(); ~Midi(); - void registerMidiCallback(CallbackFunction f); uint32_t getNumDevices(); void clearBuffer(); void pushBuffer(unsigned char byte); @@ -41,7 +40,6 @@ namespace l::hid::midi { public: MidiManagerWindows() = default; - virtual void RegisterCallback(CallbackFunction cb) override; virtual uint32_t GetNumDevices() override; virtual void SendToDevice(uint32_t deviceIndex, uint32_t status, uint32_t channel, uint32_t data1, uint32_t data2) override; protected: diff --git a/packages/nodegraph/include/nodegraph/NodeGraph.h b/packages/nodegraph/include/nodegraph/NodeGraph.h index a739f401..24840845 100644 --- a/packages/nodegraph/include/nodegraph/NodeGraph.h +++ b/packages/nodegraph/include/nodegraph/NodeGraph.h @@ -50,10 +50,12 @@ namespace l::nodegraph { float mOutput = 0.0f; std::unique_ptr> mOutputBuf = nullptr; std::unique_ptr mName = nullptr; + bool mOutputPolled = false; float& GetOutput(int32_t numSamples = 1) { if (!mOutputBuf) { if (numSamples <= 1) { + mOutputPolled = true; return mOutput; } else { @@ -63,6 +65,7 @@ namespace l::nodegraph { if (static_cast(mOutputBuf->size()) < numSamples) { mOutputBuf->resize(numSamples); } + mOutputPolled = true; return *mOutputBuf->data(); } @@ -75,6 +78,13 @@ namespace l::nodegraph { } } + bool IsOutputPolled() { + return mOutputPolled; + } + + void ResetOutputPollState() { + mOutputPolled = false; + } }; class NodeGraphBase; @@ -153,6 +163,8 @@ namespace l::nodegraph { virtual bool IsDataVisible(int8_t num); virtual bool IsDataEditable(int8_t num); + virtual bool IsOutputPolled(int8_t outputChannel); + virtual OutputType GetOutputType(); protected: diff --git a/packages/nodegraph/include/nodegraph/NodeGraphOperations.h b/packages/nodegraph/include/nodegraph/NodeGraphOperations.h index 45832af1..17c57827 100644 --- a/packages/nodegraph/include/nodegraph/NodeGraphOperations.h +++ b/packages/nodegraph/include/nodegraph/NodeGraphOperations.h @@ -947,5 +947,108 @@ namespace l::nodegraph { protected: l::hid::midi::MidiManager* mMidiManager = nullptr; }; + + /*********************************************************************/ + + enum class MidiButtonStates { + DISCONNECTED = 0, // non lit + ALLOCATED, // yellow blinking (in a node button group) + CONNECTED, // yellow (output polled) + OFF, // red, off - 0 + HALF_OFF, // red blinking - 0.33 + HALF_ON, // green blinking - 0.67 + ON, // green - 1 + }; + + class GraphInputMidiButtons : public NodeGraphOp { + public: + + // midi button commands + // 0 - not lit + // 1 - green + // 2 - green flashing + // 3 - red + // 4 - red flashing + // 5 - yellow + // 6 - yellow flashing + + // remap to + const int8_t BUTTON_DISCONNECTED = 0; // non lit - undefined + const int8_t BUTTON_ALLOCATED = 1; // yellow flashing - undefined + const int8_t BUTTON_OFF = 2; // red - 0.00f + const int8_t BUTTON_ON_AND_OFF = 3; // yellow - 0.50f + const int8_t BUTTON_ON = 4; // green - 1.00f + + const std::vector remapToButtonStatesToColor = {0, 6, 3, 5, 1, 4, 2}; + + GraphInputMidiButtons(NodeGraphBase* node, l::hid::midi::MidiManager* midiManager, int32_t buttonGroup) : + NodeGraphOp(node, 0, 8, 8), + mMidiManager(midiManager), + mButtonGroup(static_cast(buttonGroup)), + mNodeName("Midi Button Group " + std::to_string(mButtonGroup)) + { + mButtonStates.resize(8); + + if (mMidiManager) { + mCallbackId = mMidiManager->RegisterCallback([&](l::hid::midi::MidiData data) { + MidiEvent(data); + }); + + // turn all buttons on + for (int8_t i = 0; i < 8; i++) { + UpdateButton(i, BUTTON_ALLOCATED); + } + } + } + + std::string defaultOutStrings[8] = { + "Button " + std::to_string(mButtonGroup), + "Button " + std::to_string(mButtonGroup + 1), + "Button " + std::to_string(mButtonGroup + 2), + "Button " + std::to_string(mButtonGroup + 3), + "Button " + std::to_string(mButtonGroup + 4), + "Button " + std::to_string(mButtonGroup + 5), + "Button " + std::to_string(mButtonGroup + 6), + "Button " + std::to_string(mButtonGroup + 7) + }; + + virtual ~GraphInputMidiButtons() { + // turn off all buttons before destructing + for (int8_t i = 0; i < 8; i++) { + UpdateButton(i, BUTTON_DISCONNECTED); + } + + mMidiManager->UnregisterCallback(mCallbackId); + } + virtual void Reset() override; + virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; + virtual void Tick(int32_t tick, float deltaTime) override; + virtual std::string_view GetOutputName(int8_t outputChannel) override { + return defaultOutStrings[outputChannel]; + } + virtual std::string_view GetName() override { + return mNodeName; + } + virtual bool IsDataVisible(int8_t) override { + return true; + } + void UpdateButton(int8_t buttonId, int8_t buttonState) { + if (mMidiManager) { + int8_t buttonColor = remapToButtonStatesToColor.at(buttonState); + mMidiManager->SendToDevice(0, 0x90, 0, mButtonGroup * 8 + buttonId, buttonColor); + mButtonStates.at(buttonId) = buttonState; + } + } + + virtual void MidiEvent(const l::hid::midi::MidiData& data); + protected: + l::hid::midi::MidiManager* mMidiManager = nullptr; + int32_t mCallbackId = 0; + int8_t mButtonGroup; + std::string mNodeName; + bool mMidiShiftState = false; + std::vector mButtonStates; + int32_t mInitCounter = 0; + }; } diff --git a/packages/nodegraph/include/nodegraph/NodeGraphSchema.h b/packages/nodegraph/include/nodegraph/NodeGraphSchema.h index b818c0ff..aa153e48 100644 --- a/packages/nodegraph/include/nodegraph/NodeGraphSchema.h +++ b/packages/nodegraph/include/nodegraph/NodeGraphSchema.h @@ -67,6 +67,11 @@ namespace l::nodegraph { RegisterNodeType("Input", 300, "Keyboard Piano"); RegisterNodeType("Input", 301, "Midi Keyboard"); RegisterNodeType("Input", 302, "Midi Knobs"); + RegisterNodeType("Input", 303, "Midi Button Group 1"); + RegisterNodeType("Input", 304, "Midi Button Group 2"); + RegisterNodeType("Input", 305, "Midi Button Group 3"); + RegisterNodeType("Input", 306, "Midi Button Group 4"); + RegisterNodeType("Input", 307, "Midi Button Group 5"); } ~NodeGraphSchema() = default; diff --git a/packages/nodegraph/source/common/NodeGraph.cpp b/packages/nodegraph/source/common/NodeGraph.cpp index 9dbddaca..9886f917 100644 --- a/packages/nodegraph/source/common/NodeGraph.cpp +++ b/packages/nodegraph/source/common/NodeGraph.cpp @@ -243,6 +243,10 @@ namespace l::nodegraph { return false; } + bool NodeGraphBase::IsOutputPolled(int8_t outputChannel) { + return mOutputs.at(outputChannel).IsOutputPolled(); + } + OutputType NodeGraphBase::GetOutputType() { return mOutputType; } diff --git a/packages/nodegraph/source/common/NodeGraphOperations.cpp b/packages/nodegraph/source/common/NodeGraphOperations.cpp index acf13192..1fa805ac 100644 --- a/packages/nodegraph/source/common/NodeGraphOperations.cpp +++ b/packages/nodegraph/source/common/NodeGraphOperations.cpp @@ -959,7 +959,7 @@ namespace l::nodegraph { } void GraphInputMidiKeyboard::MidiEvent(const l::hid::midi::MidiData& data) { - //LOG(LogInfo) << "listener 1: dev" << data.device << " stat " << data.status << " ch " << data.channel << " d1 " << data.data1 << " d2 " << data.data2; + LOG(LogInfo) << "listener 1: dev" << data.device << " stat " << data.status << " ch " << data.channel << " d1 " << data.data1 << " d2 " << data.data2; if (data.status == 9) { // note on @@ -1051,4 +1051,56 @@ namespace l::nodegraph { } } + /*********************************************************************/ + void GraphInputMidiButtons::Reset() { + for (int8_t i = 0; i < GetNumInputs(); i++) { + mNode->SetInput(i, 0.0f); + } + } + + void GraphInputMidiButtons::Process(int32_t, std::vector& inputs, std::vector& outputs) { + for (size_t i = 0; i < inputs.size(); i++) { + outputs.at(i).mOutput = inputs.at(i).Get(); + } + } + + void GraphInputMidiButtons::Tick(int32_t, float) { + if (mInitCounter == 120){ + for (int8_t i = 0; i < 8; i++) { + if (mButtonStates.at(i) == BUTTON_ALLOCATED) { + // Set to off when allocated and output has been polled, i.e been connected and sampled + UpdateButton(i, BUTTON_OFF); + } + } + } + mInitCounter++; + } + + void GraphInputMidiButtons::MidiEvent(const l::hid::midi::MidiData& data) { + LOG(LogInfo) << "listener 1: dev" << data.device << " stat " << data.status << " ch " << data.channel << " d1 " << data.data1 << " d2 " << data.data2; + + + int8_t buttonIndex = static_cast(data.data1) - mButtonGroup * 8; + + if (data.data1 == 98) { + // shift button event + mMidiShiftState = data.status == 9 ? true : data.status == 8 ? false : mMidiShiftState; + } + else if (buttonIndex >= 0 && buttonIndex < 8) { + // pad button event + if (data.status == 9) { + if (mButtonStates.at(buttonIndex) <= BUTTON_ALLOCATED) { + // if button has not been polled before user input, set button to off first time + mButtonStates.at(buttonIndex) = BUTTON_OFF; + }else if (!mMidiShiftState && mButtonStates.at(buttonIndex) < BUTTON_ON) { + mButtonStates.at(buttonIndex)++; + }else if (mMidiShiftState && mButtonStates.at(buttonIndex) > BUTTON_OFF) { + mButtonStates.at(buttonIndex)--; + } + } + float outputValue = static_cast(mButtonStates.at(buttonIndex) - BUTTON_OFF) / 2.0f; + mNode->SetInput(buttonIndex, outputValue); + UpdateButton(buttonIndex, mButtonStates.at(buttonIndex)); + } + } } diff --git a/packages/nodegraph/source/common/NodeGraphSchema.cpp b/packages/nodegraph/source/common/NodeGraphSchema.cpp index 0e9efa30..10e9b250 100644 --- a/packages/nodegraph/source/common/NodeGraphSchema.cpp +++ b/packages/nodegraph/source/common/NodeGraphSchema.cpp @@ -124,6 +124,21 @@ namespace l::nodegraph { case 302: node = mMainNodeGraph.NewNode(OutputType::Default, mMidiManager); break; + case 303: + node = mMainNodeGraph.NewNode(OutputType::Default, mMidiManager, 0); + break; + case 304: + node = mMainNodeGraph.NewNode(OutputType::Default, mMidiManager, 1); + break; + case 305: + node = mMainNodeGraph.NewNode(OutputType::Default, mMidiManager, 2); + break; + case 306: + node = mMainNodeGraph.NewNode(OutputType::Default, mMidiManager, 3); + break; + case 307: + node = mMainNodeGraph.NewNode(OutputType::Default, mMidiManager, 4); + break; default: ASSERT(typeId < 10000) << "Custom node id's begin at id 1000"; From 40bc9b3415330037c1d8102542ef7b9a11bab9bf Mon Sep 17 00:00:00 2001 From: lnd3 Date: Sat, 7 Sep 2024 09:18:43 +0200 Subject: [PATCH 03/42] Confirm destruction is in order. --- .../nodegraph/include/nodegraph/NodeGraph.h | 21 ++++++++++++---- .../include/nodegraph/NodeGraphOperations.h | 24 +++++++++---------- .../source/common/NodeGraphOperations.cpp | 9 ++++--- 3 files changed, 31 insertions(+), 23 deletions(-) diff --git a/packages/nodegraph/include/nodegraph/NodeGraph.h b/packages/nodegraph/include/nodegraph/NodeGraph.h index 24840845..a50a9ba1 100644 --- a/packages/nodegraph/include/nodegraph/NodeGraph.h +++ b/packages/nodegraph/include/nodegraph/NodeGraph.h @@ -46,7 +46,10 @@ namespace l::nodegraph { bool IsValidInOutNum(int8_t inoutNum, size_t inoutSize); - struct NodeGraphOutput { + class NodeGraphOutput { + public: + NodeGraphOutput() = default; + float mOutput = 0.0f; std::unique_ptr> mOutputBuf = nullptr; std::unique_ptr mName = nullptr; @@ -121,8 +124,9 @@ namespace l::nodegraph { mInputs.resize(1); mOutputs.resize(1); } - - virtual ~NodeGraphBase() = default; + virtual ~NodeGraphBase() { + LOG(LogInfo) << "Node graph base destroyed"; + } virtual void Reset(); virtual void SetId(int32_t id) { mId = id; } @@ -191,11 +195,13 @@ namespace l::nodegraph { mNumOutputs(static_cast(numOutputs)), mNumConstants(static_cast(numConstants)) {} + virtual ~NodeGraphOp() { + LOG(LogInfo) << "Node operation destroyed"; + } std::string defaultInStrings[4] = { "In 1", "In 2", "In 3", "In 4" }; std::string defaultOutStrings[4] = { "Out 1", "Out 2", "Out 3", "Out 4" }; - virtual ~NodeGraphOp() = default; virtual void Reset() {} virtual void Process(int32_t, std::vector&, std::vector&) {}; virtual void Tick(int32_t, float) {} @@ -241,6 +247,9 @@ namespace l::nodegraph { SetNumOutputs(mOperation.GetNumOutputs()); SetNumConstants(mOperation.GetNumConstants()); } + virtual ~NodeGraph() { + LOG(LogInfo) << "Node destroyed"; + } virtual void SetNumInputs(int8_t numInputs) { NodeGraphBase::SetNumInputs(numInputs); @@ -327,7 +336,9 @@ namespace l::nodegraph { SetNumOutputs(1); mOutputNodes.push_back(&mOutputNode); } - ~NodeGraphGroup() = default; + ~NodeGraphGroup() { + LOG(LogInfo) << "Node group destroyed"; + } void SetNumInputs(int8_t numInputs); void SetNumOutputs(int8_t outputCount); diff --git a/packages/nodegraph/include/nodegraph/NodeGraphOperations.h b/packages/nodegraph/include/nodegraph/NodeGraphOperations.h index 17c57827..24afe859 100644 --- a/packages/nodegraph/include/nodegraph/NodeGraphOperations.h +++ b/packages/nodegraph/include/nodegraph/NodeGraphOperations.h @@ -984,11 +984,16 @@ namespace l::nodegraph { GraphInputMidiButtons(NodeGraphBase* node, l::hid::midi::MidiManager* midiManager, int32_t buttonGroup) : NodeGraphOp(node, 0, 8, 8), mMidiManager(midiManager), - mButtonGroup(static_cast(buttonGroup)), - mNodeName("Midi Button Group " + std::to_string(mButtonGroup)) + mButtonGroup(buttonGroup) { + mNodeName = "Midi Button Group "; + mNodeName += std::to_string(mButtonGroup); mButtonStates.resize(8); + for (int8_t i = 0; i < 8; i++) { + defaultOutStrings[i] = std::string("Pad ") + std::to_string(i); + } + if (mMidiManager) { mCallbackId = mMidiManager->RegisterCallback([&](l::hid::midi::MidiData data) { MidiEvent(data); @@ -1001,16 +1006,7 @@ namespace l::nodegraph { } } - std::string defaultOutStrings[8] = { - "Button " + std::to_string(mButtonGroup), - "Button " + std::to_string(mButtonGroup + 1), - "Button " + std::to_string(mButtonGroup + 2), - "Button " + std::to_string(mButtonGroup + 3), - "Button " + std::to_string(mButtonGroup + 4), - "Button " + std::to_string(mButtonGroup + 5), - "Button " + std::to_string(mButtonGroup + 6), - "Button " + std::to_string(mButtonGroup + 7) - }; + std::string defaultOutStrings[8]; virtual ~GraphInputMidiButtons() { // turn off all buttons before destructing @@ -1018,6 +1014,8 @@ namespace l::nodegraph { UpdateButton(i, BUTTON_DISCONNECTED); } + std::this_thread::yield(); + mMidiManager->UnregisterCallback(mCallbackId); } virtual void Reset() override; @@ -1044,7 +1042,7 @@ namespace l::nodegraph { protected: l::hid::midi::MidiManager* mMidiManager = nullptr; int32_t mCallbackId = 0; - int8_t mButtonGroup; + int32_t mButtonGroup; std::string mNodeName; bool mMidiShiftState = false; std::vector mButtonStates; diff --git a/packages/nodegraph/source/common/NodeGraphOperations.cpp b/packages/nodegraph/source/common/NodeGraphOperations.cpp index 1fa805ac..8d8734fe 100644 --- a/packages/nodegraph/source/common/NodeGraphOperations.cpp +++ b/packages/nodegraph/source/common/NodeGraphOperations.cpp @@ -1077,16 +1077,15 @@ namespace l::nodegraph { } void GraphInputMidiButtons::MidiEvent(const l::hid::midi::MidiData& data) { - LOG(LogInfo) << "listener 1: dev" << data.device << " stat " << data.status << " ch " << data.channel << " d1 " << data.data1 << " d2 " << data.data2; - - - int8_t buttonIndex = static_cast(data.data1) - mButtonGroup * 8; + //LOG(LogInfo) << "listener 1: dev" << data.device << " stat " << data.status << " ch " << data.channel << " d1 " << data.data1 << " d2 " << data.data2; if (data.data1 == 98) { // shift button event mMidiShiftState = data.status == 9 ? true : data.status == 8 ? false : mMidiShiftState; } - else if (buttonIndex >= 0 && buttonIndex < 8) { + + int8_t buttonIndex = static_cast(data.data1 - mButtonGroup * 8); + if (buttonIndex >= 0 && buttonIndex < 8) { // pad button event if (data.status == 9) { if (mButtonStates.at(buttonIndex) <= BUTTON_ALLOCATED) { From 8ece73a27e5d4f2c7b4f3cee8b57f01d2363f938 Mon Sep 17 00:00:00 2001 From: lnd3 Date: Sat, 7 Sep 2024 09:33:36 +0200 Subject: [PATCH 04/42] Fix midi collisions. --- .../source/common/NodeGraphOperations.cpp | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/packages/nodegraph/source/common/NodeGraphOperations.cpp b/packages/nodegraph/source/common/NodeGraphOperations.cpp index 8d8734fe..99be6c6c 100644 --- a/packages/nodegraph/source/common/NodeGraphOperations.cpp +++ b/packages/nodegraph/source/common/NodeGraphOperations.cpp @@ -293,7 +293,7 @@ namespace l::nodegraph { void GraphSourceSineFM3::Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) { float* output0 = &outputs.at(0).GetOutput(numSamples); - mSamplesUntilUpdate = l::audio::BatchUpdate(256.0f, mSamplesUntilUpdate, 0, numSamples, + mSamplesUntilUpdate = l::audio::BatchUpdate(16.0f, mSamplesUntilUpdate, 0, numSamples, [&]() { mNote = l::math::functions::max(static_cast(inputs.at(0).Get()), 0.0); mVolume = inputs.at(1).Get(); @@ -318,7 +318,7 @@ namespace l::nodegraph { for (int32_t i = start; i < end; i++) { double phaseDelta2 = mDeltaTime * mNote; - mPhaseFmod += 0.25 * phaseDelta2; + mPhaseFmod += phaseDelta2; mPhaseFmod = l::math::functions::mod(mPhaseFmod, 1.0); double modulation = fm * l::math::functions::sin(l::math::constants::PI * mPhaseFmod * 2.0); @@ -959,8 +959,10 @@ namespace l::nodegraph { } void GraphInputMidiKeyboard::MidiEvent(const l::hid::midi::MidiData& data) { - LOG(LogInfo) << "listener 1: dev" << data.device << " stat " << data.status << " ch " << data.channel << " d1 " << data.data1 << " d2 " << data.data2; - + //LOG(LogInfo) << "listener 1: dev" << data.device << " stat " << data.status << " ch " << data.channel << " d1 " << data.data1 << " d2 " << data.data2; + if (data.channel != 1) { + return; + } if (data.status == 9) { // note on NoteOn(data.data1, data.data2); @@ -1042,9 +1044,10 @@ namespace l::nodegraph { } void GraphInputMidiKnobs::MidiEvent(const l::hid::midi::MidiData& data) { + if (data.channel != 0) { + return; + } if (data.status == 11) { - //LOG(LogInfo) << "listener 1: dev" << data.device << " stat " << data.status << " ch " << data.channel << " d1 " << data.data1 << " d2 " << data.data2; - if (data.data1 >= 48 && data.data1 <= 55) { mNode->SetInput(static_cast(data.data1 - 48), data.data2 / 128.0f); } @@ -1077,8 +1080,9 @@ namespace l::nodegraph { } void GraphInputMidiButtons::MidiEvent(const l::hid::midi::MidiData& data) { - //LOG(LogInfo) << "listener 1: dev" << data.device << " stat " << data.status << " ch " << data.channel << " d1 " << data.data1 << " d2 " << data.data2; - + if (data.channel != 0) { + return; + } if (data.data1 == 98) { // shift button event mMidiShiftState = data.status == 9 ? true : data.status == 8 ? false : mMidiShiftState; From c7823c58e9fdc816284f011e3027ef15cea75320 Mon Sep 17 00:00:00 2001 From: lnd3 Date: Sat, 7 Sep 2024 19:17:50 +0200 Subject: [PATCH 05/42] 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; From 94c45cf2fd649013afcccfcbe62687a3209991f0 Mon Sep 17 00:00:00 2001 From: lnd3 Date: Sat, 7 Sep 2024 19:22:52 +0200 Subject: [PATCH 06/42] Clean up. --- .../include/nodegraph/NodeGraphOperations.h | 1 - .../nodegraph/source/common/NodeGraphOperations.cpp | 12 +++++------- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/packages/nodegraph/include/nodegraph/NodeGraphOperations.h b/packages/nodegraph/include/nodegraph/NodeGraphOperations.h index 3723058e..4c0fdf88 100644 --- a/packages/nodegraph/include/nodegraph/NodeGraphOperations.h +++ b/packages/nodegraph/include/nodegraph/NodeGraphOperations.h @@ -880,7 +880,6 @@ namespace l::nodegraph { protected: float mSamplesUntilUpdate = 0.0f; int32_t mGateIndex = 0; - std::vector mGate; float mGainTarget = 1.0f; float mGain = 1.0f; float mGateSmoothing = 0.01f; diff --git a/packages/nodegraph/source/common/NodeGraphOperations.cpp b/packages/nodegraph/source/common/NodeGraphOperations.cpp index b41b0afd..914a5d2a 100644 --- a/packages/nodegraph/source/common/NodeGraphOperations.cpp +++ b/packages/nodegraph/source/common/NodeGraphOperations.cpp @@ -891,10 +891,8 @@ namespace l::nodegraph { 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 fmod = inputs.at(3).Get(); float attack = inputs.at(4).Get(); size_t patternsSize = patterns.size(); @@ -902,18 +900,18 @@ namespace l::nodegraph { auto& gate = patterns[patternId % patternsSize]; size_t patternSize = gate.size(); - float fmod = inputs.at(3).Get() / static_cast(patternSize); - mGateIndex %= patternSize; + float fmodPerPattern = fmod / static_cast(patternSize); mGateSmoothing = attack * attack; mGateSmoothingNeg = mGateSmoothing * 0.25f; float freq = 44100.0f * 60.0f / bpm; - mSamplesUntilUpdate = l::audio::BatchUpdate(freq * fmod, mSamplesUntilUpdate, 0, numSamples, + mSamplesUntilUpdate = l::audio::BatchUpdate(freq * fmodPerPattern, mSamplesUntilUpdate, 0, numSamples, [&]() { + mGateIndex %= gate.size(); mGainTarget = gate[mGateIndex]; - mGateIndex = (mGateIndex + 1) % gate.size(); + mGateIndex++; }, [&](int32_t start, int32_t end, bool) { for (int32_t i = start; i < end; i++) { From 0d335efb538466ec3f427fe500f35310172e5e3a Mon Sep 17 00:00:00 2001 From: lnd3 Date: Mon, 9 Sep 2024 00:49:46 +0200 Subject: [PATCH 07/42] Add an arpeggio operation. Fix some math functions. --- packages/audio/include/audio/AudioUtils.h | 2 + packages/audio/source/common/AudioUtils.cpp | 5 + packages/math/include/math/MathAlgorithm.h | 7 +- packages/math/include/math/MathFunc.h | 62 +- .../include/nodegraph/NodeGraphOpEffect.h | 339 +++++ .../include/nodegraph/NodeGraphOpFilter.h | 95 ++ .../include/nodegraph/NodeGraphOpInput.h | 239 ++++ .../include/nodegraph/NodeGraphOpLogic.h | 88 ++ .../include/nodegraph/NodeGraphOpNumeric.h | 180 +++ .../include/nodegraph/NodeGraphOpOutput.h | 111 ++ .../include/nodegraph/NodeGraphOpSource.h | 252 ++++ .../include/nodegraph/NodeGraphOperations.h | 1104 ---------------- .../include/nodegraph/NodeGraphSchema.h | 9 +- .../source/common/NodeGraphOpEffect.cpp | 494 +++++++ .../source/common/NodeGraphOpFilter.cpp | 117 ++ .../source/common/NodeGraphOpInput.cpp | 241 ++++ .../source/common/NodeGraphOpLogic.cpp | 12 + .../source/common/NodeGraphOpNumeric.cpp | 12 + .../source/common/NodeGraphOpOutput.cpp | 129 ++ .../source/common/NodeGraphOpSource.cpp | 340 +++++ .../source/common/NodeGraphOperations.cpp | 1169 ----------------- .../source/common/NodeGraphSchema.cpp | 5 +- .../tests/common/NodeGraphSchemaTest.cpp | 1 - .../include/rendering/ui/UINodeEditor.h | 1 - 24 files changed, 2711 insertions(+), 2303 deletions(-) create mode 100644 packages/nodegraph/include/nodegraph/NodeGraphOpEffect.h create mode 100644 packages/nodegraph/include/nodegraph/NodeGraphOpFilter.h create mode 100644 packages/nodegraph/include/nodegraph/NodeGraphOpInput.h create mode 100644 packages/nodegraph/include/nodegraph/NodeGraphOpLogic.h create mode 100644 packages/nodegraph/include/nodegraph/NodeGraphOpNumeric.h create mode 100644 packages/nodegraph/include/nodegraph/NodeGraphOpOutput.h create mode 100644 packages/nodegraph/include/nodegraph/NodeGraphOpSource.h delete mode 100644 packages/nodegraph/include/nodegraph/NodeGraphOperations.h create mode 100644 packages/nodegraph/source/common/NodeGraphOpEffect.cpp create mode 100644 packages/nodegraph/source/common/NodeGraphOpFilter.cpp create mode 100644 packages/nodegraph/source/common/NodeGraphOpInput.cpp create mode 100644 packages/nodegraph/source/common/NodeGraphOpLogic.cpp create mode 100644 packages/nodegraph/source/common/NodeGraphOpNumeric.cpp create mode 100644 packages/nodegraph/source/common/NodeGraphOpOutput.cpp create mode 100644 packages/nodegraph/source/common/NodeGraphOpSource.cpp delete mode 100644 packages/nodegraph/source/common/NodeGraphOperations.cpp diff --git a/packages/audio/include/audio/AudioUtils.h b/packages/audio/include/audio/AudioUtils.h index 6eac5491..65199753 100644 --- a/packages/audio/include/audio/AudioUtils.h +++ b/packages/audio/include/audio/AudioUtils.h @@ -3,7 +3,9 @@ #include namespace l::audio { + extern const float gNoNote; float GetFrequencyFromNote(float note); double GetPhaseModifier(double note, double modifier); + float GetRCAFactor(float numSamples, float limit = 0.01f); float BatchUpdate(float updateSamples, float samplesLeft, int32_t start, int32_t end, std::function update, std::function process); } diff --git a/packages/audio/source/common/AudioUtils.cpp b/packages/audio/source/common/AudioUtils.cpp index ef011c65..c42e0249 100644 --- a/packages/audio/source/common/AudioUtils.cpp +++ b/packages/audio/source/common/AudioUtils.cpp @@ -8,6 +8,7 @@ #include namespace l::audio { + const float gNoNote = -500.0f; float GetFrequencyFromNote(float note) { return 440.0f * l::math::functions::pow(2.0f, (note - 49.0f) / 12.0f); @@ -18,6 +19,10 @@ namespace l::audio { return 800.0 * modifier * modifier * limit; } + float GetRCAFactor(float numSamples, float limit) { + return limit / l::math::functions::log(static_cast(numSamples)); + } + float BatchUpdate(float updateSamples, float samplesLeft, int32_t start, int32_t end, std::function update, std::function process) { float startNum = static_cast(start); while (startNum < static_cast(end)) { diff --git a/packages/math/include/math/MathAlgorithm.h b/packages/math/include/math/MathAlgorithm.h index 06408db0..a675b01f 100644 --- a/packages/math/include/math/MathAlgorithm.h +++ b/packages/math/include/math/MathAlgorithm.h @@ -29,11 +29,6 @@ namespace l::math::algorithm { return dst; } - template - bool samesign(T a, T b) { - return a * b >= 0.0; - } - template void reflect(T& d, const T& n) { auto t = d.dot(n) * 2.0; @@ -76,7 +71,7 @@ namespace l::math::algorithm { } n++; T aEval = eval(a); - if (samesign(cEval, aEval)) { + if (l::math::functions::samesign(cEval, aEval)) { a = c; } else { diff --git a/packages/math/include/math/MathFunc.h b/packages/math/include/math/MathFunc.h index 7e3e79c2..0b915b0c 100644 --- a/packages/math/include/math/MathFunc.h +++ b/packages/math/include/math/MathFunc.h @@ -11,6 +11,16 @@ namespace l::math::functions { + template + bool equal(T a, T b, T accuracy) { + return abs(a - b) < accuracy; + } + + template + bool samesign(T a, T b) { + return a * b >= 0.0; + } + template void swap(T& val1, T& val2) { T tmp = std::move(val1); @@ -139,6 +149,18 @@ namespace l::math::functions { } } + template + T log(T val) { + if constexpr (std::is_floating_point_v) { + if constexpr (sizeof(T) == 4) { + return logf(val); + } + else if constexpr (sizeof(T) == 8) { + return logl(val); + } + } + } + // source https://iquilezles.org/articles/functions/ // Almost Identity(I) @@ -147,8 +169,8 @@ namespace l::math::functions { T almostIdentity(T x, T m, T n) { if (x > m) return x; - const T a = 2.0 * n - m; - const T b = 2.0 * m - 3.0 * n; + const T a = static_cast(2.0) * n - m; + const T b = static_cast(2.0) * m - static_cast(3.0) * n; const T t = x / m; return (a * t + b) * t * t + n; } @@ -167,7 +189,7 @@ namespace l::math::functions { template T almostUnitIdentity(T x) { - return x * x * (2.0 - x); + return x * x * (static_cast(2.0) - x); } // Smoothstep Integral @@ -175,9 +197,9 @@ namespace l::math::functions { template T integralSmoothstep(T x, T t) { - if (x >= t) return x - t * 0.5; + if (x >= t) return x - t * static_cast(0.5); T f = x / t; - return f * f * f * (t - x * 0.5); + return f * f * f * (t - x * static_cast(0.5)); } // Exponential Impulse @@ -186,7 +208,7 @@ namespace l::math::functions { T expImpulse(T x, T k) { const T h = k * x; - return h * functions::exp(1.0 - h); + return h * functions::exp(static_cast(1.0) - h); } // Polynomial Impulse @@ -194,14 +216,14 @@ namespace l::math::functions { template T quaImpulse(T k, T x) { - return 2.0 * functions::sqrt(k) * x / (1.0 + k * x * x); + return static_cast(1.0) * functions::sqrt(k) * x / (static_cast(1.0) + k * x * x); } // You can easily generalize it to other powers to get different falloff shapes, where n is the degree of the polynomial : template T polyImpulse(T k, T n, T x) { - return (n / (n - 1.0)) * functions::pow((n - 1.0) * k, 1.0 / n) * x / (1.0 + k * functions::pow(x, n)); + return (n / (n - static_cast(1.0))) * functions::pow((n - static_cast(1.0)) * k, static_cast(1.0) / n) * x / (static_cast(1.0) + k * functions::pow(x, n)); } // Sustained Impulse @@ -209,8 +231,8 @@ namespace l::math::functions { template T expSustainedImpulse(T x, T f, T k) { - T s = functions::max(x - f, 0.0); - return functions::min(x * x / (f * f), 1 + (2.0 / f) * s * functions::exp(-k * s)); + T s = functions::max(x - f, static_cast(0.0)); + return functions::min(x * x / (f * f), static_cast(1.0) + (static_cast(2.0) / f) * s * functions::exp(-k * s)); } // Cubic Pulse @@ -219,9 +241,9 @@ namespace l::math::functions { T cubicPulse(T c, T w, T x) { x = functions::abs(x - c); - if (x > w) return 0.0; + if (x > w) return static_cast(0.0); x /= w; - return 1.0 - x * x * (3.0 - 2.0 * x); + return static_cast(1.0) - x * x * (static_cast(3.0) - static_cast(2.0) * x); } // Exponential Step @@ -237,8 +259,8 @@ namespace l::math::functions { template T gain(T x, T k) { - const T a = 0.5 * functions::pow(2.0 * ((x < 0.5) ? x : 1.0 - x), k); - return (x < 0.5) ? a : 1.0 - a; + const T a = static_cast(0.5) * functions::pow(static_cast(2.0) * ((x < static_cast(0.5)) ? x : static_cast(1.0) - x), k); + return (x < static_cast(0.5)) ? a : static_cast(1.0) - a; } // Parabola @@ -246,7 +268,7 @@ namespace l::math::functions { template T parabola(T x, T k) { - return functions::pow(4.0 * x * (1.0 - x), k); + return functions::pow(4.0 * x * (static_cast(1.0) - x), k); } // Power curve @@ -263,22 +285,22 @@ namespace l::math::functions { template T sinc(T x, T k) { - const T a = math::constants::PI * (k * x - 1.0); + const T a = math::constants::PI * (k * x - static_cast(1.0)); return functions::sin(a) / a; } template T sigmoid(T x, T k) { - return static_cast(1.0 / (1.0 + functions::exp(-x * k))); + return static_cast(static_cast(1.0) / (static_cast(1.0) + functions::exp(-x * k))); } template T sigmoidFast(T x) { - if (x < 1.0 && x > -1.0) { - return static_cast(x * (1.5 - 0.5 * x * x)); + if (x < static_cast(1.0) && x > static_cast(-1.0)) { + return static_cast(x * (static_cast(1.5) - static_cast(0.5) * x * x)); } else { - return static_cast(x > 0.0 ? 1.0 : -1.0); + return static_cast(x > static_cast(0.0) ? static_cast(1.0) : static_cast(-1.0)); } } } diff --git a/packages/nodegraph/include/nodegraph/NodeGraphOpEffect.h b/packages/nodegraph/include/nodegraph/NodeGraphOpEffect.h new file mode 100644 index 00000000..43d77675 --- /dev/null +++ b/packages/nodegraph/include/nodegraph/NodeGraphOpEffect.h @@ -0,0 +1,339 @@ +#pragma once +#include "nodegraph/NodeGraph.h" + +#include "logging/LoggingAll.h" + +#include "hid/KeyboardPiano.h" +#include "hid/Midi.h" + +#include "audio/PortAudio.h" +#include "audio/AudioUtils.h" + +#include "math/MathFunc.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace l::nodegraph { + + /*********************************************************************/ + class GraphEffectReverb1 : public NodeGraphOp { + public: + std::string defaultInStrings[11] = { "In 1", "In 2", "Mix", "Attenuation", "Room Size", "Delay 1", "Feedback 1", "Delay 2", "Feedback 2", "Delay 3", "Feedback 3" }; + std::string defaultOutStrings[2] = { "Out 1", "Out 2" }; + + uint32_t GetFramesPerRoomSize(float roomSize) { + const float metersPerFrame = 334.0f / 44100.0f; // (m/s)/(frames/s) = m/frames; + const float metersToWallPerFrame = metersPerFrame / 2.0f; // half the distance to wall for the bounced distance + const float framesPerRoom = roomSize / metersToWallPerFrame; + return static_cast(framesPerRoom); + } + + const float maxRoomSizeInMeters = 334.0f; // 334 meters large is 2 seconds of reverbation + + GraphEffectReverb1(NodeGraphBase * node) : + NodeGraphOp(node, 11, 2, 0) { + uint32_t bufferSize = GetFramesPerRoomSize(maxRoomSizeInMeters); + buf0.resize(bufferSize); + buf1.resize(bufferSize); + } + + virtual ~GraphEffectReverb1() = 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 "Reverb 1"; + } + + protected: + bool mInited = false; + std::vector buf0; + std::vector buf1; + uint32_t bufIndex = 0; + float mix = 0.0f; + float fb = 0.0f; + float fb0 = 0.0f; + float fb1 = 0.0f; + float fb2 = 0.0f; + float d0 = 0.0f; + float d1 = 0.0f; + float d2 = 0.0f; + + }; + + /*********************************************************************/ + class GraphEffectReverb2 : public NodeGraphOp { + public: + std::string defaultInStrings[12] = { "In 1", "In 2", "Mix", "Feedback", "Room Size", "Width", "First tap", "Longest tap", "Num taps", "Tap bulge", "Filter cutoff", "Filter res"}; + std::string defaultOutStrings[4] = { "Rev 1", "Rev 2", "Tap 1", "Tap 2"}; + + uint32_t GetFramesPerRoomSize(float roomSize) { + const float metersPerFrame = 334.0f / 44100.0f; // (m/s)/(frames/s) = m/frames; + const float metersToWallPerFrame = metersPerFrame / 2.0f; // half the distance to wall for the bounced distance + const float framesPerRoom = roomSize / metersToWallPerFrame; + return static_cast(framesPerRoom); + } + + const float maxRoomSizeInMeters = 334.0f; // 334 meters large is 2 seconds of reverbation + + GraphEffectReverb2(NodeGraphBase* node) : + NodeGraphOp(node, 12, 4, 0) + { + uint32_t bufferSize = GetFramesPerRoomSize(maxRoomSizeInMeters); + bufRev0.resize(bufferSize); + bufRev1.resize(bufferSize); + bufEarlyTap0.resize(bufferSize); + bufEarlyTap1.resize(bufferSize); + } + + virtual ~GraphEffectReverb2() = 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 "Reverb 2"; + } + + protected: + bool mInited = false; + + std::vector bufRev0; + std::vector bufRev1; + std::vector bufEarlyTap0; + std::vector bufEarlyTap1; + uint32_t bufIndex = 0; + uint32_t bufTapIndex = 0; + + std::minstd_rand mLCG; + + float mLP0 = 0.0f; + float mLP1 = 0.0f; + float mLP2 = 0.0f; + 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(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 "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(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 "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(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 "Saturator"; + } + + protected: + 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; + float mGainTarget = 1.0f; + float mGain = 1.0f; + float mGateSmoothing = 0.01f; + float mGateSmoothingNeg = 0.01f; + }; + + /*********************************************************************/ + class GraphEffectArpeggio: public NodeGraphOp { + public: + std::string defaultInStrings[6] = { "Note On Id", "Note Off Id", "Velocity", "Bpm", "Fmod", "Attack"}; + std::string defaultOutStrings[3] = { "Freq", "Volume" }; + + GraphEffectArpeggio(NodeGraphBase* node) : + NodeGraphOp(node, 6, 2, 0) + {} + + virtual ~GraphEffectArpeggio() = default; + virtual void Reset() override; + virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; + virtual bool IsDataVisible(int8_t) override { + return true; + } + virtual bool IsDataEditable(int8_t channel) override { + return channel > 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 "Arpeggio"; + } + + protected: + float mSamplesUntilUpdate = 0.0f; + float mGainTarget = 0.0f; + float mGain = 0.0f; + float mGainSmoothing = 0.01f; + float mGainSmoothingNeg = 0.01f; + + float mNoteOnId = l::audio::gNoNote; + float mNoteOffId = l::audio::gNoNote; + + float mCurrentNoteFreq = 0.0f; + std::vector mNotes; + int32_t mNoteIndex = 0; + }; +} + diff --git a/packages/nodegraph/include/nodegraph/NodeGraphOpFilter.h b/packages/nodegraph/include/nodegraph/NodeGraphOpFilter.h new file mode 100644 index 00000000..34f27be0 --- /dev/null +++ b/packages/nodegraph/include/nodegraph/NodeGraphOpFilter.h @@ -0,0 +1,95 @@ +#pragma once +#include "nodegraph/NodeGraph.h" + +#include "logging/LoggingAll.h" + +#include "hid/KeyboardPiano.h" +#include "hid/Midi.h" + +#include "audio/PortAudio.h" +#include "audio/AudioUtils.h" + +#include "math/MathFunc.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace l::nodegraph { + + /* Stateful filtering operations */ + + /*********************************************************************/ + class GraphFilterLowpass : public NodeGraphOp { + public: + std::string defaultInStrings[3] = { "Data", "Cutoff", "Resonance"}; + std::string defaultOutStrings[1] = { "Out" }; + + GraphFilterLowpass(NodeGraphBase* node) : + NodeGraphOp(node, 3, 1) + {} + + virtual ~GraphFilterLowpass() = default; + virtual void Reset() override; + virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; + virtual bool IsDataVisible(int8_t) override { return true; } + virtual bool IsDataEditable(int8_t channel) override { return channel > 0 ? 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 "Lowpass"; + } + protected: + float mState0 = 0.0f; + float mState1 = 0.0f; + }; + + /*********************************************************************/ + class GraphFilterEnvelope : public NodeGraphOp { + public: + std::string defaultInStrings[5] = { "Freq", "Velocity", "Attack", "Release", "Fade"}; + std::string defaultOutStrings[2] = { "Freq", "Volume"}; + + GraphFilterEnvelope(NodeGraphBase* node) : + NodeGraphOp(node, 5, 2) + {} + + virtual ~GraphFilterEnvelope() = default; + virtual void Reset() override; + virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; + virtual void Tick(int32_t, float) {} + 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 bool IsDataVisible(int8_t channel) override { + return channel >= 1 ? true : false; + } + virtual bool IsDataEditable(int8_t channel) override { + return channel >= 1 ? true : false; + } + virtual std::string_view GetName() override { + return "Envelope"; + } + protected: + int32_t mFrameCount = 0; + float mEnvelopeTarget = 0.0f; + float mFreq = 0.0f; + float mEnvelope = 0.0f; + }; + +} + diff --git a/packages/nodegraph/include/nodegraph/NodeGraphOpInput.h b/packages/nodegraph/include/nodegraph/NodeGraphOpInput.h new file mode 100644 index 00000000..c438e81e --- /dev/null +++ b/packages/nodegraph/include/nodegraph/NodeGraphOpInput.h @@ -0,0 +1,239 @@ +#pragma once +#include "nodegraph/NodeGraph.h" + +#include "logging/LoggingAll.h" + +#include "hid/KeyboardPiano.h" +#include "hid/Midi.h" + +#include "audio/PortAudio.h" +#include "audio/AudioUtils.h" + +#include "math/MathFunc.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace l::nodegraph { + + /*********************************************************************/ + class GraphInputKeyboardPiano : public NodeGraphOp, public l::hid::INoteProcessor { + public: + GraphInputKeyboardPiano(NodeGraphBase* node, l::hid::KeyState* keyState) : + NodeGraphOp(node, 0, 3, 3) + { + mChannel.resize(1); + mKeyboard.SetKeyState(keyState); + mKeyboard.SetNoteProcessor(this); + } + + std::string defaultOutStrings[3] = { "Freq", "Note On Id", "Note Off Id" }; + + virtual ~GraphInputKeyboardPiano() = default; + virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; + virtual void Tick(int32_t tickCount, float elapsed) override; + virtual void Reset() override; + virtual std::string_view GetOutputName(int8_t outputChannel) override { + return defaultOutStrings[outputChannel]; + } + virtual std::string_view GetName() override { + return "Keyboard"; + } + virtual bool IsDataVisible(int8_t) override { + return true; + } + virtual void NoteOn(int32_t note) override; + virtual void NoteOff() override; + virtual void NoteOff(int32_t note) override; + protected: + int8_t ResetNoteChannel(int32_t note); + int8_t GetNextNoteChannel(int32_t note); + + int8_t mNoteCounter = 0; + std::vector> mChannel; + l::hid::KeyboardPiano mKeyboard; + }; + + /*********************************************************************/ + class GraphInputMidiKeyboard : public NodeGraphOp { + public: + GraphInputMidiKeyboard(NodeGraphBase* node, l::hid::midi::MidiManager* midiManager) : + NodeGraphOp(node, 0, 4, 4), + mMidiManager(midiManager) + { + mChannel.resize(1); + + mMidiManager->RegisterCallback([&](l::hid::midi::MidiData data) { + MidiEvent(data); + }); + } + + std::string defaultOutStrings[4] = { "Freq", "Velocity", "Note On Id", "Note Off Id"}; + + virtual ~GraphInputMidiKeyboard() = default; + virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; + virtual void Reset() override; + virtual std::string_view GetOutputName(int8_t outputChannel) override { + return defaultOutStrings[outputChannel]; + } + virtual std::string_view GetName() override { + return "Midi Device"; + } + virtual bool IsDataVisible(int8_t) override { + return true; + } + + virtual void MidiEvent(const l::hid::midi::MidiData& data); + void NoteOn(int32_t note, int32_t velocity); + void NoteOff(); + void NoteOff(int32_t note); + protected: + int8_t ResetNoteChannel(int32_t note); + int8_t GetNextNoteChannel(int32_t note); + + l::hid::midi::MidiManager* mMidiManager = nullptr; + + int8_t mNoteCounter = 0; + std::vector> mChannel; + }; + + /*********************************************************************/ + class GraphInputMidiKnobs : public NodeGraphOp { + public: + GraphInputMidiKnobs(NodeGraphBase* node, l::hid::midi::MidiManager* midiManager) : + NodeGraphOp(node, 0, 8, 8), + mMidiManager(midiManager) + { + mMidiManager->RegisterCallback([&](l::hid::midi::MidiData data) { + MidiEvent(data); + }); + } + + std::string defaultOutStrings[8] = { "Knob 1", "Knob 2", "Knob 3", "Knob 4", "Knob 5", "Knob 6", "Knob 7", "Knob 8", }; + + virtual ~GraphInputMidiKnobs() = default; + virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; + virtual void Reset() override; + virtual std::string_view GetOutputName(int8_t outputChannel) override { + return defaultOutStrings[outputChannel]; + } + virtual std::string_view GetName() override { + return "Midi Knobs"; + } + virtual bool IsDataVisible(int8_t) override { + return true; + } + + virtual void MidiEvent(const l::hid::midi::MidiData& data); + protected: + l::hid::midi::MidiManager* mMidiManager = nullptr; + }; + + /*********************************************************************/ + + enum class MidiButtonStates { + DISCONNECTED = 0, // non lit + ALLOCATED, // yellow blinking (in a node button group) + CONNECTED, // yellow (output polled) + OFF, // red, off - 0 + HALF_OFF, // red blinking - 0.33 + HALF_ON, // green blinking - 0.67 + ON, // green - 1 + }; + + class GraphInputMidiButtons : public NodeGraphOp { + public: + + // midi button commands + // 0 - not lit + // 1 - green + // 2 - green flashing + // 3 - red + // 4 - red flashing + // 5 - yellow + // 6 - yellow flashing + + // remap to + const int8_t BUTTON_DISCONNECTED = 0; // non lit - undefined + const int8_t BUTTON_ALLOCATED = 1; // yellow flashing - undefined + const int8_t BUTTON_OFF = 2; // red - 0.00f + const int8_t BUTTON_ON_AND_OFF = 3; // yellow - 0.50f + const int8_t BUTTON_ON = 4; // green - 1.00f + + const std::vector remapToButtonStatesToColor = {0, 6, 3, 5, 1, 4, 2}; + + GraphInputMidiButtons(NodeGraphBase* node, l::hid::midi::MidiManager* midiManager, int32_t buttonGroup) : + NodeGraphOp(node, 0, 8, 8), + mMidiManager(midiManager), + mButtonGroup(buttonGroup) + { + mNodeName = "Midi Button Group "; + mNodeName += std::to_string(mButtonGroup); + mButtonStates.resize(8); + + for (int8_t i = 0; i < 8; i++) { + defaultOutStrings[i] = std::string("Pad ") + std::to_string(i); + } + + if (mMidiManager) { + mCallbackId = mMidiManager->RegisterCallback([&](l::hid::midi::MidiData data) { + MidiEvent(data); + }); + + // turn all buttons on + for (int8_t i = 0; i < 8; i++) { + UpdateButton(i, BUTTON_ALLOCATED); + } + } + } + + std::string defaultOutStrings[8]; + + virtual ~GraphInputMidiButtons() { + // turn off all buttons before destructing + for (int8_t i = 0; i < 8; i++) { + UpdateButton(i, BUTTON_DISCONNECTED); + } + + std::this_thread::yield(); + + mMidiManager->UnregisterCallback(mCallbackId); + } + virtual void Reset() override; + virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; + virtual void Tick(int32_t tick, float deltaTime) override; + virtual std::string_view GetOutputName(int8_t outputChannel) override { + return defaultOutStrings[outputChannel]; + } + virtual std::string_view GetName() override { + return mNodeName; + } + virtual bool IsDataVisible(int8_t) override { + return true; + } + void UpdateButton(int8_t buttonId, int8_t buttonState) { + if (mMidiManager) { + int8_t buttonColor = remapToButtonStatesToColor.at(buttonState); + mMidiManager->SendToDevice(0, 0x90, 0, mButtonGroup * 8 + buttonId, buttonColor); + mButtonStates.at(buttonId) = buttonState; + } + } + + virtual void MidiEvent(const l::hid::midi::MidiData& data); + protected: + l::hid::midi::MidiManager* mMidiManager = nullptr; + int32_t mCallbackId = 0; + int32_t mButtonGroup; + std::string mNodeName; + bool mMidiShiftState = false; + std::vector mButtonStates; + int32_t mInitCounter = 0; + }; +} + diff --git a/packages/nodegraph/include/nodegraph/NodeGraphOpLogic.h b/packages/nodegraph/include/nodegraph/NodeGraphOpLogic.h new file mode 100644 index 00000000..abba62a9 --- /dev/null +++ b/packages/nodegraph/include/nodegraph/NodeGraphOpLogic.h @@ -0,0 +1,88 @@ +#pragma once +#include "nodegraph/NodeGraph.h" + +#include "logging/LoggingAll.h" + +#include "hid/KeyboardPiano.h" +#include "hid/Midi.h" + +#include "audio/PortAudio.h" +#include "audio/AudioUtils.h" + +#include "math/MathFunc.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace l::nodegraph { + + /* Logical operations */ + + /*********************************************************************/ + class GraphLogicalAnd : public NodeGraphOp { + public: + GraphLogicalAnd(NodeGraphBase* node) : + NodeGraphOp(node, 2, 1) + {} + + virtual ~GraphLogicalAnd() = default; + virtual void Process(int32_t, std::vector& inputs, std::vector& outputs) override { + bool input1 = inputs.at(0).Get() != 0.0f; + bool input2 = inputs.at(1).Get() != 0.0f; + outputs.at(0).mOutput = (input1 && input2) ? 1.0f : 0.0f; + } + std::string_view GetName() override { + return "And"; + } + virtual bool IsDataVisible(int8_t) override { return true; } + virtual bool IsDataEditable(int8_t) override { return true; } + }; + + /*********************************************************************/ + class GraphLogicalOr : public NodeGraphOp { + public: + GraphLogicalOr(NodeGraphBase* node) : + NodeGraphOp(node, 2, 1) + {} + + virtual ~GraphLogicalOr() = default; + virtual void Process(int32_t, std::vector& inputs, std::vector& outputs) override { + bool input1 = inputs.at(0).Get() != 0.0f; + bool input2 = inputs.at(1).Get() != 0.0f; + outputs.at(0).mOutput = (input1 || input2) ? 1.0f : 0.0f; + } + std::string_view GetName() override { + return "Or"; + } + virtual bool IsDataVisible(int8_t) override { return true; } + virtual bool IsDataEditable(int8_t) override { return true; } + }; + + /*********************************************************************/ + class GraphLogicalXor : public NodeGraphOp { + public: + GraphLogicalXor(NodeGraphBase* node) : + NodeGraphOp(node, 2, 1) + {} + + virtual ~GraphLogicalXor() = default; + virtual void Process(int32_t, std::vector& inputs, std::vector& outputs) override { + bool input1 = inputs.at(0).Get() != 0.0f; + bool input2 = inputs.at(1).Get() != 0.0f; + outputs.at(0).mOutput = (input1 ^ input2) ? 1.0f : 0.0f; + } + virtual std::string_view GetName() override { + return "Xor"; + } + virtual bool IsDataVisible(int8_t) override { return true; } + virtual bool IsDataEditable(int8_t) override { return true; } + }; + +} + diff --git a/packages/nodegraph/include/nodegraph/NodeGraphOpNumeric.h b/packages/nodegraph/include/nodegraph/NodeGraphOpNumeric.h new file mode 100644 index 00000000..04edf45b --- /dev/null +++ b/packages/nodegraph/include/nodegraph/NodeGraphOpNumeric.h @@ -0,0 +1,180 @@ +#pragma once +#include "nodegraph/NodeGraph.h" + +#include "logging/LoggingAll.h" + +#include "hid/KeyboardPiano.h" +#include "hid/Midi.h" + +#include "audio/PortAudio.h" +#include "audio/AudioUtils.h" + +#include "math/MathFunc.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace l::nodegraph { + + /*********************************************************************/ + class GraphNumericAdd : public NodeGraphOp { + public: + GraphNumericAdd(NodeGraphBase* node) : + NodeGraphOp(node, 2, 1) + {} + virtual ~GraphNumericAdd() = default; + virtual void Process(int32_t, std::vector& inputs, std::vector& outputs) override { + outputs.at(0).mOutput = inputs.at(0).Get() + inputs.at(1).Get(); + } + virtual std::string_view GetName() override { + return "Add"; + } + virtual bool IsDataVisible(int8_t) override { return true; } + virtual bool IsDataEditable(int8_t) override { return true; } + }; + + /*********************************************************************/ + class GraphNumericMultiply : public NodeGraphOp { + public: + GraphNumericMultiply(NodeGraphBase* node) : + NodeGraphOp(node, 2, 1) + {} + + virtual ~GraphNumericMultiply() = default; + virtual void Process(int32_t, std::vector& inputs, std::vector& outputs) override { + outputs.at(0).mOutput = inputs.at(0).Get() * inputs.at(1).Get(); + } + virtual std::string_view GetName() override { + return "Multiply"; + } + virtual bool IsDataVisible(int8_t) override { return true; } + virtual bool IsDataEditable(int8_t) override { return true; } + }; + + /*********************************************************************/ + class GraphNumericSubtract : public NodeGraphOp { + public: + GraphNumericSubtract(NodeGraphBase* node) : + NodeGraphOp(node, 2, 1) + {} + virtual ~GraphNumericSubtract() = default; + virtual void Process(int32_t, std::vector& inputs, std::vector& outputs) override { + outputs.at(0).mOutput = inputs.at(0).Get() - inputs.at(1).Get(); + } + virtual std::string_view GetName() override { + return "Subtract"; + } + virtual bool IsDataVisible(int8_t) override { return true; } + virtual bool IsDataEditable(int8_t) override { return true; } + }; + + /*********************************************************************/ + class GraphNumericNegate : public NodeGraphOp { + public: + GraphNumericNegate(NodeGraphBase* node) : + NodeGraphOp(node, 1, 1) + {} + + virtual ~GraphNumericNegate() = default; + virtual void Process(int32_t, std::vector& inputs, std::vector& outputs) override { + outputs.at(0).mOutput = -inputs.at(0).Get(); + } + virtual std::string_view GetName() override { + return "Negate"; + } + virtual bool IsDataVisible(int8_t) override { return true; } + virtual bool IsDataEditable(int8_t) override { return true; } + }; + + /*********************************************************************/ + class GraphNumericIntegral : public NodeGraphOp { + public: + GraphNumericIntegral(NodeGraphBase* node) : + NodeGraphOp(node, 1, 1) + {} + + virtual ~GraphNumericIntegral() = default; + virtual void Reset() override { + mOutput = 0.0f; + } + + virtual void Process(int32_t, std::vector& inputs, std::vector& outputs) override { + mOutput += inputs.at(0).Get(); + outputs.at(0).mOutput = mOutput; + } + virtual std::string_view GetName() override { + return "Integral"; + } + virtual bool IsDataVisible(int8_t) override { return true; } + virtual bool IsDataEditable(int8_t) override { return true; } + protected: + float mOutput = 0.0f; + }; + + /*********************************************************************/ + class GraphNumericMultiply3 : public NodeGraphOp { + public: + GraphNumericMultiply3(NodeGraphBase* node) : + NodeGraphOp(node, 3, 1) + {} + + virtual ~GraphNumericMultiply3() = default; + virtual void Process(int32_t, std::vector& inputs, std::vector& outputs) override { + outputs.at(0).mOutput = inputs.at(0).Get() * inputs.at(1).Get() * inputs.at(2).Get(); + } + virtual std::string_view GetName() override { + return "Multiply3"; + } + virtual bool IsDataVisible(int8_t) override { return true; } + virtual bool IsDataEditable(int8_t) override { return true; } + }; + + /*********************************************************************/ + class GraphNumericMultiplyAndAdd : public NodeGraphOp { + public: + GraphNumericMultiplyAndAdd(NodeGraphBase* node) : + NodeGraphOp(node, 3, 1) + {} + + std::string defaultInStrings[3] = { "Factor 1", "Factor 2", "Term 1" }; + + virtual ~GraphNumericMultiplyAndAdd() = default; + void virtual Process(int32_t, std::vector& inputs, std::vector& outputs) override { + outputs.at(0).mOutput = inputs.at(0).Get() * inputs.at(1).Get() + inputs.at(2).Get(); + } + virtual std::string_view GetName() override { + return "Multiply & Add"; + } + + virtual std::string_view GetInputName(int8_t inputChannel) { + return defaultInStrings[inputChannel]; + } + virtual bool IsDataVisible(int8_t) override { return true; } + virtual bool IsDataEditable(int8_t) override { return true; } + }; + + /*********************************************************************/ + class GraphNumericRound : public NodeGraphOp { + public: + GraphNumericRound(NodeGraphBase* node) : + NodeGraphOp(node, 1, 1) + {} + virtual ~GraphNumericRound() = default; + virtual void Process(int32_t, std::vector& inputs, std::vector& outputs) override { + outputs.at(0).mOutput = l::math::functions::round(inputs.at(0).Get()); + } + virtual std::string_view GetName() override { + return "Round"; + } + virtual bool IsDataVisible(int8_t) override { return true; } + virtual bool IsDataEditable(int8_t) override { return true; } + }; + +} + diff --git a/packages/nodegraph/include/nodegraph/NodeGraphOpOutput.h b/packages/nodegraph/include/nodegraph/NodeGraphOpOutput.h new file mode 100644 index 00000000..8557b6a8 --- /dev/null +++ b/packages/nodegraph/include/nodegraph/NodeGraphOpOutput.h @@ -0,0 +1,111 @@ +#pragma once +#include "nodegraph/NodeGraph.h" + +#include "logging/LoggingAll.h" + +#include "hid/KeyboardPiano.h" +#include "hid/Midi.h" + +#include "audio/PortAudio.h" +#include "audio/AudioUtils.h" + +#include "math/MathFunc.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace l::nodegraph { + + /*********************************************************************/ + class GraphOutputDebug : public NodeGraphOp { + public: + std::string defaultInStrings[2] = { "Debug", "Smooth" }; + GraphOutputDebug(NodeGraphBase* node) : + NodeGraphOp(node, 1, 0, 2) + {} + virtual ~GraphOutputDebug() = default; + + virtual void Reset() override; + virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; + virtual bool IsDataVisible(int8_t) override { + return true; + } + virtual bool IsDataEditable(int8_t channel) override { + return channel == 1; + } + virtual std::string_view GetOutputName(int8_t outputChannel) override { + return defaultInStrings[outputChannel]; + } + virtual std::string_view GetName() override { + return "Debug"; + } + protected: + float mValue = 0.0F; + }; + + /*********************************************************************/ + class GraphOutputSpeaker : public NodeGraphOp { + public: + std::string defaultInStrings[3] = { "Left", "Right", "Volume"}; + + GraphOutputSpeaker(NodeGraphBase* node, l::audio::AudioStream* stream = nullptr) : + NodeGraphOp(node, 3, 0, 0), + mAudioStream(stream), + mCurrentStereoPosition(0) + {} + virtual ~GraphOutputSpeaker() = default; + + virtual void Reset() override; + virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; + virtual bool IsDataVisible(int8_t) override { + return true; + } + virtual bool IsDataEditable(int8_t channel) override { + return channel == 2 ? true : false; + } + virtual std::string_view GetInputName(int8_t outputChannel) override { + return defaultInStrings[outputChannel]; + } + virtual std::string_view GetName() override { + return "Speaker"; + } + + protected: + l::audio::AudioStream* mAudioStream; + int32_t mCurrentStereoPosition; + float mEnvelope = 0.0f; + }; + + /*********************************************************************/ + class GraphOutputPlot : public NodeGraphOp { + public: + std::string defaultInStrings[2] = { "Plot", "Smooth" }; + GraphOutputPlot(NodeGraphBase* node, int32_t plotSamples) : + NodeGraphOp(node, 1, 1, 0), + mPlotSamples(plotSamples) + { + mNode->GetOutput(0, mPlotSamples); + } + virtual ~GraphOutputPlot() = default; + + virtual void Reset() override; + virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; + virtual std::string_view GetOutputName(int8_t outputChannel) override { + return defaultInStrings[outputChannel]; + } + virtual std::string_view GetName() override { + return "Plot"; + } + protected: + int32_t mPlotSamples = 50; + int32_t mCurIndex = 0; + }; + +} + diff --git a/packages/nodegraph/include/nodegraph/NodeGraphOpSource.h b/packages/nodegraph/include/nodegraph/NodeGraphOpSource.h new file mode 100644 index 00000000..e07a62f4 --- /dev/null +++ b/packages/nodegraph/include/nodegraph/NodeGraphOpSource.h @@ -0,0 +1,252 @@ +#pragma once +#include "nodegraph/NodeGraph.h" + +#include "logging/LoggingAll.h" + +#include "hid/KeyboardPiano.h" +#include "hid/Midi.h" + +#include "audio/PortAudio.h" +#include "audio/AudioUtils.h" + +#include "math/MathFunc.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace l::nodegraph { + + /* Mathematical operations */ + + /*********************************************************************/ + class GraphSourceConstants : public NodeGraphOp { + public: + GraphSourceConstants(NodeGraphBase* node, int32_t mode) : + NodeGraphOp(node, 0, 4, 4), + mMode(mode) + {} + + virtual ~GraphSourceConstants() = default; + virtual void Reset(); + virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; + virtual void Tick(int32_t, float) override; + virtual std::string_view GetName() override { + switch (mMode) { + case 0: + return "Constant [0,1]"; + case 1: + return "Constant [-1,1]"; + case 2: + return "Constant [0,100]"; + case 3: + return "Constant [-inf,inf]"; + }; + return ""; + } + virtual bool IsDataVisible(int8_t) override {return true;} + virtual bool IsDataEditable(int8_t) override {return true;} + protected: + int32_t mMode; + float mMax = 1.0f; + float mMin = 0.0f; + }; + + /*********************************************************************/ + class GraphSourceTime : public NodeGraphOp { + public: + GraphSourceTime(NodeGraphBase* node) : + NodeGraphOp(node, 0, 2, 0) + {} + + std::string defaultOutStrings[2] = { "Audio Time", "Frame Time"}; + + virtual ~GraphSourceTime() = default; + virtual void Reset() override; + virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; + virtual void Tick(int32_t, float) override; + virtual std::string_view GetOutputName(int8_t outputChannel) override { + return defaultOutStrings[outputChannel]; + } + virtual std::string_view GetName() override { + return "Time"; + } + virtual bool IsDataVisible(int8_t) override { return true; } + virtual bool IsDataEditable(int8_t) override { return true; } + protected: + float mAudioTime = 0.0f; + float mFrameTime = 0.0f; + }; + + /*********************************************************************/ + class GraphSourceSine : public NodeGraphOp { + public: + GraphSourceSine(NodeGraphBase* node) : + NodeGraphOp(node, 5, 1) + {} + + std::string defaultInStrings[5] = { "Freq", "Volume", "Fmod", "Phase", "Reset"}; + std::string defaultOutStrings[1] = { "Out"}; + + virtual ~GraphSourceSine() = default; + virtual void Reset() override; + virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; + virtual bool IsDataVisible(int8_t) override { return true; } + virtual bool IsDataEditable(int8_t) override { return true; } + 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 "Sine FM"; + } + protected: + double mFreq = 0.0f; + float mVolume = 0.0f; + double mFmod = 0.0f; + double mPmod = 0.0f; + float mReset = 0.0f; + + double mWave = 0.0f; + double mDeltaTime = 0.0f; + float mVol = 0.0f; + + double mPhase = 0.0f; + double mPhaseMod = 0.0f; + float mSamplesUntilUpdate = 0.0f; + }; + + /*********************************************************************/ + class GraphSourceSineFM : public NodeGraphOp { + public: + GraphSourceSineFM(NodeGraphBase* node) : + NodeGraphOp(node, 8, 1) + {} + + std::string defaultInStrings[8] = { "Freq", "Volume", "Fmod", "FmodFreq", "FmodVol", "FmodOfs", "FmodGain", "Reset"}; + std::string defaultOutStrings[1] = { "Out" }; + + virtual ~GraphSourceSineFM() = default; + virtual void Reset() override; + virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; + virtual bool IsDataVisible(int8_t) override { return true; } + virtual bool IsDataEditable(int8_t) override { return true; } + 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 "Sine FM"; + } + protected: + double mFreq = 0.0; + float mVolume = 0.0f; + double mFmod = 0.0; + double mPmod = 0.0; + float mReset = 0.0f; + + double mWave = 0.0; + double mDeltaTime = 0.0; + float mVol = 0.0f; + + double mPhase = 0.0; + + + double mPhaseFmod = 0.0; + double mFmodFrq = 0.0; + double mFmodVol = 0.0; + double mFmodOfs = 0.0; + + float mSamplesUntilUpdate = 0.0f; + }; + + /*********************************************************************/ + class GraphSourceSineFM2 : public NodeGraphOp { + public: + GraphSourceSineFM2(NodeGraphBase* node) : + NodeGraphOp(node, 5, 1) + {} + + std::string defaultInStrings[5] = { "Freq", "Volume", "FmodVol", "FmodFreq", "Reset" }; + std::string defaultOutStrings[1] = { "Out" }; + + virtual ~GraphSourceSineFM2() = default; + virtual void Reset() override; + virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; + virtual bool IsDataVisible(int8_t) override { return true; } + virtual bool IsDataEditable(int8_t) override { return true; } + 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 "Sine FM 2"; + } + protected: + double mFreq = 0.0; + float mVolume = 0.0f; + double mFmod = 0.0; + double mPmod = 0.0; + float mReset = 0.0f; + + double mWave = 0.0; + double mDeltaTime = 0.0; + double mDeltaLimit = 0.0; + float mVol = 0.0f; + + double mPhase = 0.0; + double mPhaseFmod = 0.0; + float mSamplesUntilUpdate = 0.0f; + }; + + /*********************************************************************/ + class GraphSourceSineFM3 : public NodeGraphOp { + public: + GraphSourceSineFM3(NodeGraphBase* node) : + NodeGraphOp(node, 4, 1) + {} + + std::string defaultInStrings[4] = { "Freq", "Volume", "Fmod", "Reset" }; + std::string defaultOutStrings[1] = { "Out" }; + + virtual ~GraphSourceSineFM3() = default; + virtual void Reset() override; + virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; + virtual bool IsDataVisible(int8_t) override { return true; } + virtual bool IsDataEditable(int8_t) override { return true; } + 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 "Sine FM 3"; + } + protected: + double mFreq = 0.0; + float mVolume = 0.0f; + double mPhaseFmod = 0.0; + float mReset = 0.0f; + + double mWave = 0.0; + double mDeltaTime = 0.0; + double mDeltaLimit = 0.0; + float mVol = 0.0f; + double mPhase = 0.0; + float mSamplesUntilUpdate = 0.0f; + }; + +} + diff --git a/packages/nodegraph/include/nodegraph/NodeGraphOperations.h b/packages/nodegraph/include/nodegraph/NodeGraphOperations.h deleted file mode 100644 index 4c0fdf88..00000000 --- a/packages/nodegraph/include/nodegraph/NodeGraphOperations.h +++ /dev/null @@ -1,1104 +0,0 @@ -#pragma once -#include "nodegraph/NodeGraph.h" - -#include "logging/LoggingAll.h" - -#include "hid/KeyboardPiano.h" -#include "hid/Midi.h" - -#include "audio/PortAudio.h" - -#include "math/MathFunc.h" - -#include -#include -#include -#include -#include -#include -#include - -namespace l::nodegraph { - - /* Mathematical operations */ - - /*********************************************************************/ - class GraphSourceConstants : public NodeGraphOp { - public: - GraphSourceConstants(NodeGraphBase* node, int32_t mode) : - NodeGraphOp(node, 0, 4, 4), - mMode(mode) - {} - - virtual ~GraphSourceConstants() = default; - virtual void Reset(); - virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; - virtual void Tick(int32_t, float) override; - virtual std::string_view GetName() override { - switch (mMode) { - case 0: - return "Constant [0,1]"; - case 1: - return "Constant [-1,1]"; - case 2: - return "Constant [0,100]"; - case 3: - return "Constant [-inf,inf]"; - }; - return ""; - } - virtual bool IsDataVisible(int8_t) override {return true;} - virtual bool IsDataEditable(int8_t) override {return true;} - protected: - int32_t mMode; - float mMax = 1.0f; - float mMin = 0.0f; - }; - - /*********************************************************************/ - class GraphSourceTime : public NodeGraphOp { - public: - GraphSourceTime(NodeGraphBase* node) : - NodeGraphOp(node, 0, 2, 0) - {} - - std::string defaultOutStrings[2] = { "Audio Time", "Frame Time"}; - - virtual ~GraphSourceTime() = default; - virtual void Reset() override; - virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; - virtual void Tick(int32_t, float) override; - virtual std::string_view GetOutputName(int8_t outputChannel) override { - return defaultOutStrings[outputChannel]; - } - virtual std::string_view GetName() override { - return "Time"; - } - virtual bool IsDataVisible(int8_t) override { return true; } - virtual bool IsDataEditable(int8_t) override { return true; } - protected: - float mAudioTime = 0.0f; - float mFrameTime = 0.0f; - }; - - /*********************************************************************/ - class GraphSourceSine : public NodeGraphOp { - public: - GraphSourceSine(NodeGraphBase* node) : - NodeGraphOp(node, 5, 1) - {} - - std::string defaultInStrings[5] = { "Note", "Volume", "Fmod", "Phase", "Reset"}; - std::string defaultOutStrings[1] = { "Out"}; - - virtual ~GraphSourceSine() = default; - virtual void Reset() override; - virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; - virtual bool IsDataVisible(int8_t) override { return true; } - virtual bool IsDataEditable(int8_t) override { return true; } - 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 "Sine FM"; - } - protected: - double mNote = 0.0f; - float mVolume = 0.0f; - double mFmod = 0.0f; - double mPmod = 0.0f; - float mReset = 0.0f; - - double mWave = 0.0f; - double mDeltaTime = 0.0f; - float mVol = 0.0f; - - double mPhase = 0.0f; - double mPhaseMod = 0.0f; - float mSamplesUntilUpdate = 0.0f; - }; - - /*********************************************************************/ - class GraphSourceSineFM : public NodeGraphOp { - public: - GraphSourceSineFM(NodeGraphBase* node) : - NodeGraphOp(node, 8, 1) - {} - - std::string defaultInStrings[8] = { "Note", "Volume", "Fmod", "FmodFreq", "FmodVol", "FmodOfs", "FmodGain", "Reset"}; - std::string defaultOutStrings[1] = { "Out" }; - - virtual ~GraphSourceSineFM() = default; - virtual void Reset() override; - virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; - virtual bool IsDataVisible(int8_t) override { return true; } - virtual bool IsDataEditable(int8_t) override { return true; } - 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 "Sine FM"; - } - protected: - double mNote = 0.0; - float mVolume = 0.0f; - double mFmod = 0.0; - double mPmod = 0.0; - float mReset = 0.0f; - - double mWave = 0.0; - double mDeltaTime = 0.0; - float mVol = 0.0f; - - double mPhase = 0.0; - - - double mPhaseFmod = 0.0; - double mFmodFrq = 0.0; - double mFmodVol = 0.0; - double mFmodOfs = 0.0; - - float mSamplesUntilUpdate = 0.0f; - }; - - /*********************************************************************/ - class GraphSourceSineFM2 : public NodeGraphOp { - public: - GraphSourceSineFM2(NodeGraphBase* node) : - NodeGraphOp(node, 5, 1) - {} - - std::string defaultInStrings[5] = { "Note", "Volume", "FmodVol", "FmodFreq", "Reset" }; - std::string defaultOutStrings[1] = { "Out" }; - - virtual ~GraphSourceSineFM2() = default; - virtual void Reset() override; - virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; - virtual bool IsDataVisible(int8_t) override { return true; } - virtual bool IsDataEditable(int8_t) override { return true; } - 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 "Sine FM 2"; - } - protected: - double mNote = 0.0; - float mVolume = 0.0f; - double mFmod = 0.0; - double mPmod = 0.0; - float mReset = 0.0f; - - double mWave = 0.0; - double mDeltaTime = 0.0; - double mDeltaLimit = 0.0; - float mVol = 0.0f; - - double mPhase = 0.0; - double mPhaseFmod = 0.0; - float mSamplesUntilUpdate = 0.0f; - }; - - /*********************************************************************/ - class GraphSourceSineFM3 : public NodeGraphOp { - public: - GraphSourceSineFM3(NodeGraphBase* node) : - NodeGraphOp(node, 4, 1) - {} - - std::string defaultInStrings[4] = { "Note", "Volume", "Fmod", "Reset" }; - std::string defaultOutStrings[1] = { "Out" }; - - virtual ~GraphSourceSineFM3() = default; - virtual void Reset() override; - virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; - virtual bool IsDataVisible(int8_t) override { return true; } - virtual bool IsDataEditable(int8_t) override { return true; } - 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 "Sine FM 3"; - } - protected: - double mNote = 0.0; - float mVolume = 0.0f; - double mPhaseFmod = 0.0; - float mReset = 0.0f; - - double mWave = 0.0; - double mDeltaTime = 0.0; - double mDeltaLimit = 0.0; - float mVol = 0.0f; - double mPhase = 0.0; - float mSamplesUntilUpdate = 0.0f; - }; - - /*********************************************************************/ - class GraphNumericAdd : public NodeGraphOp { - public: - GraphNumericAdd(NodeGraphBase* node) : - NodeGraphOp(node, 2, 1) - {} - virtual ~GraphNumericAdd() = default; - virtual void Process(int32_t, std::vector& inputs, std::vector& outputs) override { - outputs.at(0).mOutput = inputs.at(0).Get() + inputs.at(1).Get(); - } - virtual std::string_view GetName() override { - return "Add"; - } - virtual bool IsDataVisible(int8_t) override { return true; } - virtual bool IsDataEditable(int8_t) override { return true; } - }; - - /*********************************************************************/ - class GraphNumericMultiply : public NodeGraphOp { - public: - GraphNumericMultiply(NodeGraphBase* node) : - NodeGraphOp(node, 2, 1) - {} - - virtual ~GraphNumericMultiply() = default; - virtual void Process(int32_t, std::vector& inputs, std::vector& outputs) override { - outputs.at(0).mOutput = inputs.at(0).Get() * inputs.at(1).Get(); - } - virtual std::string_view GetName() override { - return "Multiply"; - } - virtual bool IsDataVisible(int8_t) override { return true; } - virtual bool IsDataEditable(int8_t) override { return true; } - }; - - /*********************************************************************/ - class GraphNumericSubtract : public NodeGraphOp { - public: - GraphNumericSubtract(NodeGraphBase* node) : - NodeGraphOp(node, 2, 1) - {} - virtual ~GraphNumericSubtract() = default; - virtual void Process(int32_t, std::vector& inputs, std::vector& outputs) override { - outputs.at(0).mOutput = inputs.at(0).Get() - inputs.at(1).Get(); - } - virtual std::string_view GetName() override { - return "Subtract"; - } - virtual bool IsDataVisible(int8_t) override { return true; } - virtual bool IsDataEditable(int8_t) override { return true; } - }; - - /*********************************************************************/ - class GraphNumericNegate : public NodeGraphOp { - public: - GraphNumericNegate(NodeGraphBase* node) : - NodeGraphOp(node, 1, 1) - {} - - virtual ~GraphNumericNegate() = default; - virtual void Process(int32_t, std::vector& inputs, std::vector& outputs) override { - outputs.at(0).mOutput = -inputs.at(0).Get(); - } - virtual std::string_view GetName() override { - return "Negate"; - } - virtual bool IsDataVisible(int8_t) override { return true; } - virtual bool IsDataEditable(int8_t) override { return true; } - }; - - /*********************************************************************/ - class GraphNumericIntegral : public NodeGraphOp { - public: - GraphNumericIntegral(NodeGraphBase* node) : - NodeGraphOp(node, 1, 1) - {} - - virtual ~GraphNumericIntegral() = default; - virtual void Reset() override { - mOutput = 0.0f; - } - - virtual void Process(int32_t, std::vector& inputs, std::vector& outputs) override { - mOutput += inputs.at(0).Get(); - outputs.at(0).mOutput = mOutput; - } - virtual std::string_view GetName() override { - return "Integral"; - } - virtual bool IsDataVisible(int8_t) override { return true; } - virtual bool IsDataEditable(int8_t) override { return true; } - protected: - float mOutput = 0.0f; - }; - - /*********************************************************************/ - class GraphNumericMultiply3 : public NodeGraphOp { - public: - GraphNumericMultiply3(NodeGraphBase* node) : - NodeGraphOp(node, 3, 1) - {} - - virtual ~GraphNumericMultiply3() = default; - virtual void Process(int32_t, std::vector& inputs, std::vector& outputs) override { - outputs.at(0).mOutput = inputs.at(0).Get() * inputs.at(1).Get() * inputs.at(2).Get(); - } - virtual std::string_view GetName() override { - return "Multiply3"; - } - virtual bool IsDataVisible(int8_t) override { return true; } - virtual bool IsDataEditable(int8_t) override { return true; } - }; - - /*********************************************************************/ - class GraphNumericMultiplyAndAdd : public NodeGraphOp { - public: - GraphNumericMultiplyAndAdd(NodeGraphBase* node) : - NodeGraphOp(node, 3, 1) - {} - - std::string defaultInStrings[3] = { "Factor 1", "Factor 2", "Term 1" }; - - virtual ~GraphNumericMultiplyAndAdd() = default; - void virtual Process(int32_t, std::vector& inputs, std::vector& outputs) override { - outputs.at(0).mOutput = inputs.at(0).Get() * inputs.at(1).Get() + inputs.at(2).Get(); - } - virtual std::string_view GetName() override { - return "Multiply & Add"; - } - - virtual std::string_view GetInputName(int8_t inputChannel) { - return defaultInStrings[inputChannel]; - } - virtual bool IsDataVisible(int8_t) override { return true; } - virtual bool IsDataEditable(int8_t) override { return true; } - }; - - /*********************************************************************/ - class GraphNumericRound : public NodeGraphOp { - public: - GraphNumericRound(NodeGraphBase* node) : - NodeGraphOp(node, 1, 1) - {} - virtual ~GraphNumericRound() = default; - virtual void Process(int32_t, std::vector& inputs, std::vector& outputs) override { - outputs.at(0).mOutput = l::math::functions::round(inputs.at(0).Get()); - } - virtual std::string_view GetName() override { - return "Round"; - } - virtual bool IsDataVisible(int8_t) override { return true; } - virtual bool IsDataEditable(int8_t) override { return true; } - }; - - /* Logical operations */ - - /*********************************************************************/ - class GraphLogicalAnd : public NodeGraphOp { - public: - GraphLogicalAnd(NodeGraphBase* node) : - NodeGraphOp(node, 2, 1) - {} - - virtual ~GraphLogicalAnd() = default; - virtual void Process(int32_t, std::vector& inputs, std::vector& outputs) override { - bool input1 = inputs.at(0).Get() != 0.0f; - bool input2 = inputs.at(1).Get() != 0.0f; - outputs.at(0).mOutput = (input1 && input2) ? 1.0f : 0.0f; - } - std::string_view GetName() override { - return "And"; - } - virtual bool IsDataVisible(int8_t) override { return true; } - virtual bool IsDataEditable(int8_t) override { return true; } - }; - - /*********************************************************************/ - class GraphLogicalOr : public NodeGraphOp { - public: - GraphLogicalOr(NodeGraphBase* node) : - NodeGraphOp(node, 2, 1) - {} - - virtual ~GraphLogicalOr() = default; - virtual void Process(int32_t, std::vector& inputs, std::vector& outputs) override { - bool input1 = inputs.at(0).Get() != 0.0f; - bool input2 = inputs.at(1).Get() != 0.0f; - outputs.at(0).mOutput = (input1 || input2) ? 1.0f : 0.0f; - } - std::string_view GetName() override { - return "Or"; - } - virtual bool IsDataVisible(int8_t) override { return true; } - virtual bool IsDataEditable(int8_t) override { return true; } - }; - - /*********************************************************************/ - class GraphLogicalXor : public NodeGraphOp { - public: - GraphLogicalXor(NodeGraphBase* node) : - NodeGraphOp(node, 2, 1) - {} - - virtual ~GraphLogicalXor() = default; - virtual void Process(int32_t, std::vector& inputs, std::vector& outputs) override { - bool input1 = inputs.at(0).Get() != 0.0f; - bool input2 = inputs.at(1).Get() != 0.0f; - outputs.at(0).mOutput = (input1 ^ input2) ? 1.0f : 0.0f; - } - virtual std::string_view GetName() override { - return "Xor"; - } - virtual bool IsDataVisible(int8_t) override { return true; } - virtual bool IsDataEditable(int8_t) override { return true; } - }; - - /* Stateful filtering operations */ - - /*********************************************************************/ - class GraphFilterLowpass : public NodeGraphOp { - public: - std::string defaultInStrings[3] = { "Data", "Cutoff", "Resonance"}; - std::string defaultOutStrings[1] = { "Out" }; - - GraphFilterLowpass(NodeGraphBase* node) : - NodeGraphOp(node, 3, 1) - {} - - virtual ~GraphFilterLowpass() = default; - virtual void Reset() override; - virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; - virtual bool IsDataVisible(int8_t) override { return true; } - virtual bool IsDataEditable(int8_t channel) override { return channel > 0 ? 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 "Lowpass"; - } - protected: - float mState0 = 0.0f; - float mState1 = 0.0f; - }; - - /*********************************************************************/ - class GraphFilterEnvelope : public NodeGraphOp { - public: - std::string defaultInStrings[5] = { "Note", "Velocity", "Attack", "Release", "Fade"}; - std::string defaultOutStrings[2] = { "Note", "Volume"}; - - GraphFilterEnvelope(NodeGraphBase* node) : - NodeGraphOp(node, 5, 2) - {} - - virtual ~GraphFilterEnvelope() = default; - virtual void Reset() override; - virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; - virtual void Tick(int32_t, float) {} - 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 bool IsDataVisible(int8_t channel) override { - return channel >= 1 ? true : false; - } - virtual bool IsDataEditable(int8_t channel) override { - return channel >= 1 ? true : false; - } - virtual std::string_view GetName() override { - return "Envelope"; - } - protected: - uint32_t mFrameCount = 0; - float mEnvelopeTarget = 0.0f; - float mNote = 0.0f; - float mEnvelope = 0.0f; - }; - - /*********************************************************************/ - class GraphOutputDebug : public NodeGraphOp { - public: - std::string defaultInStrings[2] = { "Debug", "Smooth" }; - GraphOutputDebug(NodeGraphBase* node) : - NodeGraphOp(node, 1, 0, 2) - {} - virtual ~GraphOutputDebug() = default; - - virtual void Reset() override; - virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; - virtual bool IsDataVisible(int8_t) override { - return true; - } - virtual bool IsDataEditable(int8_t channel) override { - return channel == 1; - } - virtual std::string_view GetOutputName(int8_t outputChannel) override { - return defaultInStrings[outputChannel]; - } - virtual std::string_view GetName() override { - return "Debug"; - } - protected: - float mValue = 0.0F; - }; - - /*********************************************************************/ - class GraphOutputSpeaker : public NodeGraphOp { - public: - std::string defaultInStrings[3] = { "Left", "Right", "Volume"}; - - GraphOutputSpeaker(NodeGraphBase* node, l::audio::AudioStream* stream = nullptr) : - NodeGraphOp(node, 3, 0, 0), - mAudioStream(stream), - mCurrentStereoPosition(0) - {} - virtual ~GraphOutputSpeaker() = default; - - virtual void Reset() override; - virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; - virtual bool IsDataVisible(int8_t) override { - return true; - } - virtual bool IsDataEditable(int8_t channel) override { - return channel == 2 ? true : false; - } - virtual std::string_view GetInputName(int8_t outputChannel) override { - return defaultInStrings[outputChannel]; - } - virtual std::string_view GetName() override { - return "Speaker"; - } - - protected: - l::audio::AudioStream* mAudioStream; - int32_t mCurrentStereoPosition; - float mEnvelope = 0.0f; - }; - - /*********************************************************************/ - class GraphOutputPlot : public NodeGraphOp { - public: - std::string defaultInStrings[2] = { "Plot", "Smooth" }; - GraphOutputPlot(NodeGraphBase* node, int32_t plotSamples) : - NodeGraphOp(node, 1, 1, 0), - mPlotSamples(plotSamples) - { - mNode->GetOutput(0, mPlotSamples); - } - virtual ~GraphOutputPlot() = default; - - virtual void Reset() override; - virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; - virtual std::string_view GetOutputName(int8_t outputChannel) override { - return defaultInStrings[outputChannel]; - } - virtual std::string_view GetName() override { - return "Plot"; - } - protected: - int32_t mPlotSamples = 50; - int32_t mCurIndex = 0; - }; - - /*********************************************************************/ - class GraphEffectReverb1 : public NodeGraphOp { - public: - std::string defaultInStrings[11] = { "In 1", "In 2", "Mix", "Attenuation", "Room Size", "Delay 1", "Feedback 1", "Delay 2", "Feedback 2", "Delay 3", "Feedback 3" }; - std::string defaultOutStrings[2] = { "Out 1", "Out 2" }; - - uint32_t GetFramesPerRoomSize(float roomSize) { - const float metersPerFrame = 334.0f / 44100.0f; // (m/s)/(frames/s) = m/frames; - const float metersToWallPerFrame = metersPerFrame / 2.0f; // half the distance to wall for the bounced distance - const float framesPerRoom = roomSize / metersToWallPerFrame; - return static_cast(framesPerRoom); - } - - const float maxRoomSizeInMeters = 334.0f; // 334 meters large is 2 seconds of reverbation - - GraphEffectReverb1(NodeGraphBase * node) : - NodeGraphOp(node, 11, 2, 0) { - uint32_t bufferSize = GetFramesPerRoomSize(maxRoomSizeInMeters); - buf0.resize(bufferSize); - buf1.resize(bufferSize); - } - - virtual ~GraphEffectReverb1() = 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 "Reverb 1"; - } - - protected: - bool mInited = false; - std::vector buf0; - std::vector buf1; - uint32_t bufIndex = 0; - float mix = 0.0f; - float fb = 0.0f; - float fb0 = 0.0f; - float fb1 = 0.0f; - float fb2 = 0.0f; - float d0 = 0.0f; - float d1 = 0.0f; - float d2 = 0.0f; - - }; - - /*********************************************************************/ - class GraphEffectReverb2 : public NodeGraphOp { - public: - std::string defaultInStrings[12] = { "In 1", "In 2", "Mix", "Feedback", "Room Size", "Width", "First tap", "Longest tap", "Num taps", "Tap bulge", "Filter cutoff", "Filter res"}; - std::string defaultOutStrings[4] = { "Rev 1", "Rev 2", "Tap 1", "Tap 2"}; - - uint32_t GetFramesPerRoomSize(float roomSize) { - const float metersPerFrame = 334.0f / 44100.0f; // (m/s)/(frames/s) = m/frames; - const float metersToWallPerFrame = metersPerFrame / 2.0f; // half the distance to wall for the bounced distance - const float framesPerRoom = roomSize / metersToWallPerFrame; - return static_cast(framesPerRoom); - } - - const float maxRoomSizeInMeters = 334.0f; // 334 meters large is 2 seconds of reverbation - - GraphEffectReverb2(NodeGraphBase* node) : - NodeGraphOp(node, 12, 4, 0) - { - uint32_t bufferSize = GetFramesPerRoomSize(maxRoomSizeInMeters); - bufRev0.resize(bufferSize); - bufRev1.resize(bufferSize); - bufEarlyTap0.resize(bufferSize); - bufEarlyTap1.resize(bufferSize); - } - - virtual ~GraphEffectReverb2() = 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 "Reverb 2"; - } - - protected: - bool mInited = false; - - std::vector bufRev0; - std::vector bufRev1; - std::vector bufEarlyTap0; - std::vector bufEarlyTap1; - uint32_t bufIndex = 0; - uint32_t bufTapIndex = 0; - - std::minstd_rand mLCG; - - float mLP0 = 0.0f; - float mLP1 = 0.0f; - float mLP2 = 0.0f; - 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(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 "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(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 "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(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 "Saturator"; - } - - protected: - 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; - 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: - GraphInputKeyboardPiano(NodeGraphBase* node, int32_t polyphony, l::hid::KeyState* keyState) : - NodeGraphOp(node, 0, polyphony, polyphony) - { - mChannel.resize(polyphony); - mKeyboard.SetKeyState(keyState); - mKeyboard.SetNoteProcessor(this); - } - - std::string defaultOutStrings[8] = { "Note 1", "Note 2", "Note 3", "Note 4", "Note 5", "Note 6", "Note 7", "Note 8" }; - - virtual ~GraphInputKeyboardPiano() = default; - virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; - virtual void Tick(int32_t tickCount, float elapsed) override; - virtual void Reset() override; - virtual std::string_view GetOutputName(int8_t outputChannel) override { - return defaultOutStrings[outputChannel]; - } - virtual std::string_view GetName() override { - return "Keyboard"; - } - virtual bool IsDataVisible(int8_t) override { - return true; - } - virtual void NoteOn(int32_t note) override; - virtual void NoteOff() override; - virtual void NoteOff(int32_t note) override; - protected: - int8_t ResetNoteChannel(int32_t note); - int8_t GetNextNoteChannel(int32_t note); - - int8_t mNoteCounter = 0; - std::vector> mChannel; - l::hid::KeyboardPiano mKeyboard; - }; - - /*********************************************************************/ - class GraphInputMidiKeyboard : public NodeGraphOp { - public: - GraphInputMidiKeyboard(NodeGraphBase* node, l::hid::midi::MidiManager* midiManager) : - NodeGraphOp(node, 0, 2, 2), - mMidiManager(midiManager) - { - mChannel.resize(1); - - mMidiManager->RegisterCallback([&](l::hid::midi::MidiData data) { - MidiEvent(data); - }); - } - - std::string defaultOutStrings[2] = { "Note", "Velocity"}; - - virtual ~GraphInputMidiKeyboard() = default; - virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; - virtual void Reset() override; - virtual std::string_view GetOutputName(int8_t outputChannel) override { - return defaultOutStrings[outputChannel]; - } - virtual std::string_view GetName() override { - return "Midi Device"; - } - virtual bool IsDataVisible(int8_t) override { - return true; - } - - virtual void MidiEvent(const l::hid::midi::MidiData& data); - void NoteOn(int32_t note, int32_t velocity); - void NoteOff(); - void NoteOff(int32_t note); - protected: - int8_t ResetNoteChannel(int32_t note); - int8_t GetNextNoteChannel(int32_t note); - - l::hid::midi::MidiManager* mMidiManager = nullptr; - - int8_t mNoteCounter = 0; - std::vector> mChannel; - }; - - /*********************************************************************/ - class GraphInputMidiKnobs : public NodeGraphOp { - public: - GraphInputMidiKnobs(NodeGraphBase* node, l::hid::midi::MidiManager* midiManager) : - NodeGraphOp(node, 0, 8, 8), - mMidiManager(midiManager) - { - mMidiManager->RegisterCallback([&](l::hid::midi::MidiData data) { - MidiEvent(data); - }); - } - - std::string defaultOutStrings[8] = { "Knob 1", "Knob 2", "Knob 3", "Knob 4", "Knob 5", "Knob 6", "Knob 7", "Knob 8", }; - - virtual ~GraphInputMidiKnobs() = default; - virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; - virtual void Reset() override; - virtual std::string_view GetOutputName(int8_t outputChannel) override { - return defaultOutStrings[outputChannel]; - } - virtual std::string_view GetName() override { - return "Midi Knobs"; - } - virtual bool IsDataVisible(int8_t) override { - return true; - } - - virtual void MidiEvent(const l::hid::midi::MidiData& data); - protected: - l::hid::midi::MidiManager* mMidiManager = nullptr; - }; - - /*********************************************************************/ - - enum class MidiButtonStates { - DISCONNECTED = 0, // non lit - ALLOCATED, // yellow blinking (in a node button group) - CONNECTED, // yellow (output polled) - OFF, // red, off - 0 - HALF_OFF, // red blinking - 0.33 - HALF_ON, // green blinking - 0.67 - ON, // green - 1 - }; - - class GraphInputMidiButtons : public NodeGraphOp { - public: - - // midi button commands - // 0 - not lit - // 1 - green - // 2 - green flashing - // 3 - red - // 4 - red flashing - // 5 - yellow - // 6 - yellow flashing - - // remap to - const int8_t BUTTON_DISCONNECTED = 0; // non lit - undefined - const int8_t BUTTON_ALLOCATED = 1; // yellow flashing - undefined - const int8_t BUTTON_OFF = 2; // red - 0.00f - const int8_t BUTTON_ON_AND_OFF = 3; // yellow - 0.50f - const int8_t BUTTON_ON = 4; // green - 1.00f - - const std::vector remapToButtonStatesToColor = {0, 6, 3, 5, 1, 4, 2}; - - GraphInputMidiButtons(NodeGraphBase* node, l::hid::midi::MidiManager* midiManager, int32_t buttonGroup) : - NodeGraphOp(node, 0, 8, 8), - mMidiManager(midiManager), - mButtonGroup(buttonGroup) - { - mNodeName = "Midi Button Group "; - mNodeName += std::to_string(mButtonGroup); - mButtonStates.resize(8); - - for (int8_t i = 0; i < 8; i++) { - defaultOutStrings[i] = std::string("Pad ") + std::to_string(i); - } - - if (mMidiManager) { - mCallbackId = mMidiManager->RegisterCallback([&](l::hid::midi::MidiData data) { - MidiEvent(data); - }); - - // turn all buttons on - for (int8_t i = 0; i < 8; i++) { - UpdateButton(i, BUTTON_ALLOCATED); - } - } - } - - std::string defaultOutStrings[8]; - - virtual ~GraphInputMidiButtons() { - // turn off all buttons before destructing - for (int8_t i = 0; i < 8; i++) { - UpdateButton(i, BUTTON_DISCONNECTED); - } - - std::this_thread::yield(); - - mMidiManager->UnregisterCallback(mCallbackId); - } - virtual void Reset() override; - virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; - virtual void Tick(int32_t tick, float deltaTime) override; - virtual std::string_view GetOutputName(int8_t outputChannel) override { - return defaultOutStrings[outputChannel]; - } - virtual std::string_view GetName() override { - return mNodeName; - } - virtual bool IsDataVisible(int8_t) override { - return true; - } - void UpdateButton(int8_t buttonId, int8_t buttonState) { - if (mMidiManager) { - int8_t buttonColor = remapToButtonStatesToColor.at(buttonState); - mMidiManager->SendToDevice(0, 0x90, 0, mButtonGroup * 8 + buttonId, buttonColor); - mButtonStates.at(buttonId) = buttonState; - } - } - - virtual void MidiEvent(const l::hid::midi::MidiData& data); - protected: - l::hid::midi::MidiManager* mMidiManager = nullptr; - int32_t mCallbackId = 0; - int32_t mButtonGroup; - std::string mNodeName; - bool mMidiShiftState = false; - std::vector mButtonStates; - int32_t mInitCounter = 0; - }; -} - diff --git a/packages/nodegraph/include/nodegraph/NodeGraphSchema.h b/packages/nodegraph/include/nodegraph/NodeGraphSchema.h index 164eb0ae..3536d8b2 100644 --- a/packages/nodegraph/include/nodegraph/NodeGraphSchema.h +++ b/packages/nodegraph/include/nodegraph/NodeGraphSchema.h @@ -3,7 +3,13 @@ #include "logging/LoggingAll.h" #include "nodegraph/NodeGraph.h" -#include "nodegraph/NodeGraphOperations.h" +#include "nodegraph/NodeGraphOpEffect.h" +#include "nodegraph/NodeGraphOpFilter.h" +#include "nodegraph/NodeGraphOpInput.h" +#include "nodegraph/NodeGraphOpLogic.h" +#include "nodegraph/NodeGraphOpNumeric.h" +#include "nodegraph/NodeGraphOpOutput.h" +#include "nodegraph/NodeGraphOpSource.h" #include "hid/Midi.h" @@ -65,6 +71,7 @@ namespace l::nodegraph { RegisterNodeType("Effect", 254, "Envelope Follower"); RegisterNodeType("Effect", 255, "Saturator"); RegisterNodeType("Effect", 256, "Trance Gate"); + RegisterNodeType("Effect", 257, "Arpeggio"); RegisterNodeType("Input", 300, "Keyboard Piano"); RegisterNodeType("Input", 301, "Midi Keyboard"); RegisterNodeType("Input", 302, "Midi Knobs"); diff --git a/packages/nodegraph/source/common/NodeGraphOpEffect.cpp b/packages/nodegraph/source/common/NodeGraphOpEffect.cpp new file mode 100644 index 00000000..ca79be2e --- /dev/null +++ b/packages/nodegraph/source/common/NodeGraphOpEffect.cpp @@ -0,0 +1,494 @@ +#include "nodegraph/NodeGraphOpEffect.h" + +#include "logging/Log.h" +#include "audio/AudioUtils.h" + +#include "math/MathFunc.h" + +#include + +namespace l::nodegraph { + + /*********************************************************************/ + void GraphEffectReverb1::Reset() { + // { "In 1", "In 2", "Mix", "Attenuation", "Room Size", "Delay 1", "Feedback 1", "Delay 2", "Feedback 2", "Delay 3", "Feedback 3" }; + + mNode->SetInput(2, 0.75f); + mNode->SetInput(3, 0.5f); + mNode->SetInput(4, 30.0f); + mNode->SetInput(5, 0.5f); + mNode->SetInput(6, 0.9f); + mNode->SetInput(7, 0.8f); + mNode->SetInput(8, 0.9f); + mNode->SetInput(9, 0.7f); + mNode->SetInput(10, 0.9f); + mNode->SetInputBound(2, InputBound::INPUT_0_TO_1); + mNode->SetInputBound(3, InputBound::INPUT_0_TO_1); + mNode->SetInputBound(4, InputBound::INPUT_CUSTOM, 0.2f, maxRoomSizeInMeters); + mNode->SetInputBound(5, InputBound::INPUT_0_TO_1); + mNode->SetInputBound(6, InputBound::INPUT_0_TO_1); + mNode->SetInputBound(7, InputBound::INPUT_0_TO_1); + mNode->SetInputBound(8, InputBound::INPUT_0_TO_1); + mNode->SetInputBound(9, InputBound::INPUT_0_TO_1); + mNode->SetInputBound(10, InputBound::INPUT_0_TO_1); + } + + void GraphEffectReverb1::Process(int32_t, std::vector&inputs, std::vector&outputs) { + float wet = inputs.at(2).Get(); + + fb = 0.2f * (1.0f - inputs.at(3).Get()); + + float roomSize = inputs.at(4).Get(); + uint32_t bufSizeLimit = GetFramesPerRoomSize(roomSize); + + d0 = inputs.at(5).Get(); + fb0 = 0.5f * 0.5f * math::functions::max(inputs.at(6).Get(), 1.0f); + d1 = inputs.at(7).Get(); + fb1 = 0.5f * 0.5f * math::functions::max(inputs.at(8).Get(), 1.0f); + d2 = inputs.at(9).Get(); + fb2 = 0.5f * 0.5f * math::functions::max(inputs.at(10).Get(), 1.0f); + + float dry = 1.0f - wet; + + uint32_t delay0 = (int(bufIndex + d0 * bufSizeLimit)) % bufSizeLimit; + uint32_t delay1 = (int(bufIndex + d1 * bufSizeLimit)) % bufSizeLimit; + uint32_t delay2 = (int(bufIndex + d2 * bufSizeLimit)) % bufSizeLimit; + float in0 = inputs.at(0).Get(); + float in1 = inputs.at(1).Get(); + + outputs[0].mOutput = in0 * dry + (fb1 * buf1[delay1] + fb0 * buf0[delay0] + fb2 * buf0[delay2]) * wet; + outputs[1].mOutput = in1 * dry + (fb1 * buf0[delay1] + fb0 * buf1[delay0] + fb2 * buf1[delay2]) * wet; + + buf0[bufIndex] = fb * buf1[bufIndex] - fb1 * buf1[delay1] - fb0 * buf0[delay0] - fb2 * buf0[delay2] + in0; + buf1[bufIndex] = fb * buf0[bufIndex] - fb1 * buf0[delay1] - fb0 * buf1[delay0] - fb2 * buf1[delay2] + in1; + + bufIndex = (bufIndex + 1) % bufSizeLimit; + + delay0 = (delay0 + 1) % bufSizeLimit; + delay1 = (delay1 + 1) % bufSizeLimit; + delay2 = (delay2 + 1) % bufSizeLimit; + } + + /*********************************************************************/ + + 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); + mNode->SetInput(5, 0.5f); + mNode->SetInput(6, 0.1f); + mNode->SetInput(7, 0.8f); + mNode->SetInput(8, 5.0f); + mNode->SetInput(9, 0.7f); + mNode->SetInput(10, 0.95f); + mNode->SetInput(11, 0.01f); + mNode->SetInputBound(2, InputBound::INPUT_0_TO_1); + mNode->SetInputBound(3, InputBound::INPUT_0_TO_1); + mNode->SetInputBound(4, InputBound::INPUT_CUSTOM, 1.0f, 334.0f); + mNode->SetInputBound(5, InputBound::INPUT_0_TO_1); + mNode->SetInputBound(6, InputBound::INPUT_CUSTOM, 0.0f, 10.0f); + mNode->SetInputBound(7, InputBound::INPUT_CUSTOM, 0.0f, 10.0f); + mNode->SetInputBound(8, InputBound::INPUT_CUSTOM, 1.0f, 30.0f); + mNode->SetInputBound(9, InputBound::INPUT_CUSTOM, 1.0f, 10.0f); + mNode->SetInputBound(10, InputBound::INPUT_CUSTOM, 0.001f, 0.999f); + mNode->SetInputBound(11, InputBound::INPUT_CUSTOM, 0.001f, 0.999f); + } + + void GraphEffectReverb2::Process(int32_t, std::vector& inputs, std::vector& outputs) { + float wet = inputs.at(2).Get(); + float reverbFeedback = inputs.at(3).Get(); + float roomSize = inputs.at(4).Get(); + float stereoWidth = inputs.at(5).Get(); + float earliestDelay = inputs.at(6).Get(); + float longestDelay = inputs.at(7).Get(); + float numDelays = inputs.at(8).Get(); + float tapBulge = inputs.at(9).Get(); + float cutoff = inputs.at(10).Get(); + float res = inputs.at(11).Get(); + + if (roomSize > maxRoomSizeInMeters) { + roomSize = maxRoomSizeInMeters; + mNode->SetInput(3, maxRoomSizeInMeters); + } + else if (roomSize < 0.1f) { + roomSize = 0.1f; + mNode->SetInput(3, 0.1f); + } + uint32_t bufSizeLimit = GetFramesPerRoomSize(roomSize); + + // feedback and delay + + float dry = 1.0f - wet; + + // indexes for delays with at least one index back + float in0 = inputs.at(0).Get(); + float in1 = inputs.at(1).Get(); + + auto echoStrength = [](float x, float earlyBulgeStrength) { + // f(x) = E * 2 * x * pow(E, -x * 2); + return 2.72f * x * earlyBulgeStrength * powf(2.72f, -x * earlyBulgeStrength); + }; + + auto sample = [&](float* buf0, float* buf1, float earliestDelay, float longestDelay, float numDelays, float attenuation, float stereoWidth) { + float delayLength = longestDelay - earliestDelay; + float delayValue0 = 0.0f; + float delayValue1 = 0.0f; + + float attenuationSum = 0.01f; + float stereoMixing = stereoWidth; + for (int i = 0; i < static_cast(numDelays); i++) { + //float random = mLCG() / static_cast(INT32_MAX); + + float x = 0.5f / numDelays + i / numDelays; + float delay0 = earliestDelay + delayLength * x * x; + float delay1 = delay0; + + uint32_t delayIndex0 = (int(bufIndex + 2 * bufSizeLimit - 1 - delay0 * bufSizeLimit)) % bufSizeLimit; + uint32_t delayIndex1 = (int(bufIndex + 2 * bufSizeLimit - 1 - delay1 * bufSizeLimit)) % bufSizeLimit; + + float monoMixing = 1.0f - stereoMixing; + float gain = echoStrength(x, tapBulge); + delayValue0 += gain * (stereoMixing * buf0[delayIndex0] + monoMixing * buf1[delayIndex0]); + delayValue1 += gain * (stereoMixing * buf1[delayIndex1] + monoMixing * buf0[delayIndex1]); + + attenuationSum += gain; + } + + float gainAdjust = attenuation / attenuationSum; + + // normalize reverb based on input attenuation value + delayValue0 *= gainAdjust; + delayValue1 *= gainAdjust; + + return std::tuple(delayValue0, delayValue1); + }; + + + auto [delay0, delay1] = sample(bufEarlyTap0.data(), bufEarlyTap1.data(), earliestDelay, longestDelay, numDelays, reverbFeedback, stereoWidth); + + bufEarlyTap0[bufTapIndex] = reverbFeedback * (bufEarlyTap0[bufTapIndex] + delay0 + in0); + bufEarlyTap1[bufTapIndex] = reverbFeedback * (bufEarlyTap1[bufTapIndex] + delay1 + in1); + + bufTapIndex = (bufTapIndex + 1) % bufSizeLimit; + + outputs[2].mOutput = in0 * dry + bufEarlyTap0[bufTapIndex] * wet; + outputs[3].mOutput = in1 * dry + bufEarlyTap1[bufTapIndex] * wet; + + bufRev0[bufIndex] += delay0; + bufRev1[bufIndex] += delay1; + + // buffer blur of oldest sample plus next oldest sample with global attenuation and new input + auto [reverb0, reverb1] = sample(bufRev0.data(), bufRev1.data(), earliestDelay, longestDelay, numDelays, reverbFeedback, stereoWidth); + + cutoff *= cutoff; + float rc = 1.0f - res * cutoff; + + mLP0 = rc * mLP0 - cutoff * (mLP1 + reverb0); + mLP1 = rc * mLP1 + cutoff * mLP0; + mLP2 = rc * mLP2 - cutoff * (mLP3 + reverb1); + mLP3 = rc * mLP3 + cutoff * mLP2; + + bufRev0[bufIndex] = reverbFeedback * (bufRev0[bufIndex] + mLP1); + bufRev1[bufIndex] = reverbFeedback * (bufRev1[bufIndex] + mLP3); + + bufIndex = (bufIndex + 1) % bufSizeLimit; + + outputs[0].mOutput = in0 * dry + bufRev0[bufIndex] * wet; + outputs[1].mOutput = in1 * dry + bufRev1[bufIndex] * wet; + + // cross delay mixing (delay 1 & 2) + //buf0[bufIndex] += fb1 * buf1[delay1]; + //buf1[bufIndex] += fb0 * buf0[delay0]; + + // central delay mixing (delay 3) + //float centralDelay = fb2 * (buf0[delay2] + buf1[delay2]); + //buf0[bufIndex] += centralDelay; + //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(int32_t, 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 = inVal0 > inVal1 ? 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(int32_t, 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 = in0 > in1 ? 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(int32_t, 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(); + + float inPreamp0 = in0 * preamp; + float inPreamp1 = in1 * preamp; + + if (inPreamp0 >= limit || inPreamp0 <= -limit) { + if (inPreamp0 > 0.0f) { + inPreamp0 = limit + (1.0f - limit) * l::math::functions::sigmoidFast((inPreamp0 - limit) / ((1.0f - limit) * 1.5f)); + } + else { + inPreamp0 = -(limit + (1.0f - limit) * l::math::functions::sigmoidFast((-inPreamp0 - limit) / ((1.0f - limit) * 1.5f))); + } + } + if (inPreamp1 >= limit || inPreamp1 <= -limit) { + if (inPreamp1 > 0.0f) { + inPreamp1 = limit + (1.0f - limit) * l::math::functions::sigmoidFast((inPreamp1 - limit) / ((1.0f - limit) * 1.5f)); + } + else { + inPreamp1 = -(limit + (1.0f - limit) * l::math::functions::sigmoidFast((-inPreamp1 - limit) / ((1.0f - limit) * 1.5f))); + } + } + + outputs.at(0).mOutput = dry * in0 + wet * inPreamp0; + 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(); + float fmod = inputs.at(3).Get(); + 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 fmodPerPattern = fmod / static_cast(patternSize); + + mGateSmoothing = attack * attack; + mGateSmoothingNeg = mGateSmoothing * 0.25f; + + float freq = 44100.0f * 60.0f / bpm; + + mSamplesUntilUpdate = l::audio::BatchUpdate(freq * fmodPerPattern, mSamplesUntilUpdate, 0, numSamples, + [&]() { + mGateIndex %= gate.size(); + mGainTarget = gate[mGateIndex]; + mGateIndex++; + }, + [&](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 GraphEffectArpeggio::Reset() { + // { "Note On Id", "Note Off Id", "Velocity", "Bpm", "Fmod", "Attack"}; + + mGainTarget = 0.0f; + + mNode->SetInput(0, l::audio::gNoNote); + mNode->SetInput(1, l::audio::gNoNote); + mNode->SetInput(2, 1.0f); + mNode->SetInput(3, 60.0f); + mNode->SetInput(4, 1.0f); + mNode->SetInput(5, 0.01f); + + mNode->SetInputBound(2, InputBound::INPUT_0_TO_1); + mNode->SetInputBound(3, InputBound::INPUT_CUSTOM, 1.0f, 1000.0f); + mNode->SetInputBound(4, InputBound::INPUT_CUSTOM, 0.01f, 1.0f); + mNode->SetInputBound(5, InputBound::INPUT_0_TO_1); + } + + void GraphEffectArpeggio::Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) { + // "Note On Id", "Note Off Id", "Velocity", "Bpm", "Fmod", "Attack" + + float noteOnId = inputs.at(0).Get(); + float noteOffId = inputs.at(1).Get(); + float velocity = inputs.at(2).Get(); + float bpm = inputs.at(3).Get(); + float fmod = inputs.at(4).Get(); + float attack = inputs.at(5).Get(); + + if (noteOffId > l::audio::gNoNote) { + if (!l::math::functions::equal(mNoteOffId, noteOffId, 0.1f) && !mNotes.empty()) { + auto it = std::find_if(mNotes.begin(), mNotes.end(), [&](const float& note) { + return l::math::functions::equal(note, noteOffId, 0.1f); + }); + if (it != mNotes.end()) { + mNotes.erase(it); + mNoteOffId = noteOffId; + mNoteOnId = -l::math::constants::FLTMAX; + } + } + } + if (noteOnId > l::audio::gNoNote) { + if (!l::math::functions::equal(mNoteOnId, noteOnId, 0.1f)) { + if (mNotes.empty()) { + mSamplesUntilUpdate = 0.0f; + } + auto it = std::find_if(mNotes.begin(), mNotes.end(), [&](const float& note) { + return l::math::functions::equal(note, noteOnId, 0.1f); + }); + if (it == mNotes.end()) { + mNotes.push_back(noteOnId); + mNoteOnId = noteOnId; + mNoteOffId = -l::math::constants::FLTMAX; + } + } + } + + mGainSmoothing = attack * attack; + mGainSmoothingNeg = mGainSmoothing * 0.25f; + + float freq = 44100.0f * 60.0f / bpm; + + mSamplesUntilUpdate = l::audio::BatchUpdate(freq * fmod, mSamplesUntilUpdate, 0, numSamples, + [&]() { + if (mNotes.empty()) { + mGainTarget = 0.0f; + } + else { + mNoteIndex = mNoteIndex % mNotes.size(); + mCurrentNoteFreq = l::audio::GetFrequencyFromNote(mNotes.at(mNoteIndex)); + mNoteIndex++; + mGainTarget = velocity; + } + }, + [&](int32_t start, int32_t end, bool) { + for (int32_t i = start; i < end; i++) { + float delta = mGainTarget - mGain; + if (delta > 0) { + mGain += mGainSmoothing * delta; + } + else { + mGain += mGainSmoothingNeg * delta; + } + + outputs.at(0).mOutput = mCurrentNoteFreq; + outputs.at(1).mOutput = mGain; + } + }); + } + +} diff --git a/packages/nodegraph/source/common/NodeGraphOpFilter.cpp b/packages/nodegraph/source/common/NodeGraphOpFilter.cpp new file mode 100644 index 00000000..0cd9af55 --- /dev/null +++ b/packages/nodegraph/source/common/NodeGraphOpFilter.cpp @@ -0,0 +1,117 @@ +#include "nodegraph/NodeGraphOpFilter.h" + +#include "logging/Log.h" +#include "audio/AudioUtils.h" + +#include "math/MathFunc.h" + +#include + +namespace l::nodegraph { + + /* Stateful filtering operations */ + + /*********************************************************************/ + void GraphFilterLowpass::Reset() { + mState0 = 0.0f; + mState1 = 0.0f; + mNode->SetInput(1, 0.99f); + mNode->SetInput(2, 0.01f); + mNode->SetInputBound(1, InputBound::INPUT_0_TO_1); + mNode->SetInputBound(2, InputBound::INPUT_0_TO_1); + } + + void GraphFilterLowpass::Process(int32_t, std::vector& inputs, std::vector& outputs) { + float inputValue = inputs.at(0).Get(); + float cutoff = inputs.at(1).Get(); + float resonance = 1.0f - inputs.at(2).Get(); + + cutoff *= cutoff; + float rc = 1.0f - resonance * cutoff; + + mState0 = rc * mState0 - cutoff * (mState1 + inputValue); + mState1 = rc * mState1 + cutoff * mState0; + + outputs.at(0).mOutput = -mState1; + } + + /*********************************************************************/ + void GraphFilterEnvelope::Reset() { + mEnvelope = 0.0f; + mNode->SetInput(1, 0.5f); + mNode->SetInput(2, 50.0f); + mNode->SetInput(3, 50.0f); + mNode->SetInput(4, 0.1f); + mNode->SetInputBound(1, InputBound::INPUT_0_TO_1); + mNode->SetInputBound(2, InputBound::INPUT_CUSTOM, 1.0f, 100000.0f); + mNode->SetInputBound(3, InputBound::INPUT_CUSTOM, 1.0f, 100000.0f); + mNode->SetInputBound(4, InputBound::INPUT_CUSTOM, 0.0001f, 1.0f); + } + + void GraphFilterEnvelope::Process(int32_t, std::vector& inputs, std::vector& outputs) { + float freqTarget = inputs.at(0).Get(); + float velocity = l::math::functions::pow(inputs.at(1).Get(), 0.5f); + int32_t attackFrames = static_cast(inputs.at(2).Get() * 44100.0f / 1000.0f); + int32_t releaseFrames = static_cast(inputs.at(3).Get() * 44100.0f / 1000.0f); + float freqFade = inputs.at(4).Get(); + + float attackFade = 1.0f - l::math::functions::pow(0.001f, 1.0f / (inputs.at(2).Get() * 44100.0f * 0.001f)); + float releaseFade = 1.0f - l::math::functions::pow(0.001f, 1.0f / (inputs.at(3).Get() * 44100.0f * 0.001f)); + + if (freqTarget == 0.0f) { + // trigger release + if (mFrameCount > 0 && mFrameCount < attackFrames + 1) { + mFrameCount = attackFrames + 2; + } + else if (mFrameCount > attackFrames + 1 + releaseFrames) { + mFrameCount = 0; + } + } + else { + if (mFrameCount == 0) { + mFreq = freqTarget; + } + else { + mFrameCount = attackFrames + 2; + mEnvelopeTarget = velocity; + } + } + + if (freqTarget != 0 && mFrameCount < attackFrames) { + // attack + mEnvelopeTarget = velocity; + mFrameCount++; + } + else if (freqTarget != 0 && mFrameCount == attackFrames + 1){ + // sustain + } + else if (freqTarget == 0 && mFrameCount > attackFrames + 1) { + // release + mEnvelopeTarget = 0.0f; + mFrameCount++; + if (mFrameCount > attackFrames + 1 + releaseFrames) { + mFrameCount = 0; + } + } + + float delta = mEnvelopeTarget - mEnvelope; + if (delta > 0) { + mEnvelope += attackFade * delta; + } + else { + mEnvelope += releaseFade * delta; + } + + if (freqTarget != 0.0f) { + // note on + mFreq += freqFade * freqFade * (freqTarget - mFreq); + } + else { + // note off + } + + outputs.at(0).mOutput = mFreq; + outputs.at(1).mOutput = l::math::functions::pow(mEnvelope, 0.5f); + } + +} diff --git a/packages/nodegraph/source/common/NodeGraphOpInput.cpp b/packages/nodegraph/source/common/NodeGraphOpInput.cpp new file mode 100644 index 00000000..c0f97822 --- /dev/null +++ b/packages/nodegraph/source/common/NodeGraphOpInput.cpp @@ -0,0 +1,241 @@ +#include "nodegraph/NodeGraphOpInput.h" + +#include "logging/Log.h" +#include "audio/AudioUtils.h" + +#include "math/MathFunc.h" + +#include + +namespace l::nodegraph { + + /*********************************************************************/ + void GraphInputKeyboardPiano::Process(int32_t, std::vector& inputs, std::vector& outputs) { + for (size_t i = 0; i < inputs.size(); i++) { + outputs.at(i).mOutput = inputs.at(i).Get(); + } + } + + void GraphInputKeyboardPiano::Tick(int32_t, float) { + mKeyboard.Update(); + } + + void GraphInputKeyboardPiano::Reset() { + mNode->SetInput(0, 0.0f); + mNode->SetInput(1, l::audio::gNoNote); + mNode->SetInput(2, l::audio::gNoNote); + } + + void GraphInputKeyboardPiano::NoteOn(int32_t note) { + float frequency = l::audio::GetFrequencyFromNote(static_cast(note)); + mNode->SetInput(0, frequency); + mNode->SetInput(1, static_cast(note)); + } + void GraphInputKeyboardPiano::NoteOff() { + Reset(); + } + + void GraphInputKeyboardPiano::NoteOff(int32_t note) { + mNode->SetInput(2, static_cast(note)); + } + + int8_t GraphInputKeyboardPiano::ResetNoteChannel(int32_t note) { + for (size_t i = 0; i < mChannel.size(); i++) { + if (mChannel.at(i).first == note) { + mChannel.at(i).second = 0; + return static_cast(i); + } + } + // It is possible to get a note off for a note not playing because the channel was taken for another newer note + return -1; + } + + int8_t GraphInputKeyboardPiano::GetNextNoteChannel(int32_t note) { + for (size_t i = 0; i < mChannel.size(); i++) { + if (mChannel.at(i).first == note) { + mChannel.at(i).second = mNoteCounter++; + return static_cast(i); + } + } + + for (size_t i = 0; i < mChannel.size(); i++) { + if (mChannel.at(i).first == 0) { + mChannel.at(i).first = note; + mChannel.at(i).second = mNoteCounter++; + return static_cast(i); + } + } + + int32_t lowestCount = INT32_MAX; + int8_t lowestCountIndex = 0; + for (size_t i = 0; i < mChannel.size(); i++) { + if (lowestCount > mChannel.at(i).second) { + lowestCount = mChannel.at(i).second; + lowestCountIndex = static_cast(i); + } + } + mChannel.at(lowestCountIndex).first = note; + mChannel.at(lowestCountIndex).second = mNoteCounter++; + return lowestCountIndex; + } + + /*********************************************************************/ + void GraphInputMidiKeyboard::Process(int32_t, std::vector& inputs, std::vector& outputs) { + for (size_t i = 0; i < inputs.size(); i++) { + outputs.at(i).mOutput = inputs.at(i).Get(); + } + } + + void GraphInputMidiKeyboard::Reset() { + mNode->SetInput(0, 0.0f); + mNode->SetInput(1, 0.0f); + mNode->SetInput(2, l::audio::gNoNote); + mNode->SetInput(3, l::audio::gNoNote); + } + + void GraphInputMidiKeyboard::MidiEvent(const l::hid::midi::MidiData& data) { + //LOG(LogInfo) << "listener 1: dev" << data.device << " stat " << data.status << " ch " << data.channel << " d1 " << data.data1 << " d2 " << data.data2; + if (data.channel != 1) { + return; + } + if (data.status == 9) { + // note on + NoteOn(data.data1, data.data2); + } + else if (data.status == 8) { + // note off + NoteOff(data.data1); + } + } + + void GraphInputMidiKeyboard::NoteOn(int32_t note, int32_t velocity) { + float frequency = l::audio::GetFrequencyFromNote(static_cast(note)); + mNode->SetInput(0, frequency); + mNode->SetInput(1, velocity / 128.0f); + mNode->SetInput(2, static_cast(note)); + } + void GraphInputMidiKeyboard::NoteOff() { + Reset(); + } + + void GraphInputMidiKeyboard::NoteOff(int32_t note) { + mNode->SetInput(3, static_cast(note)); + } + + int8_t GraphInputMidiKeyboard::ResetNoteChannel(int32_t note) { + for (size_t i = 0; i < mChannel.size(); i++) { + if (mChannel.at(i).first == note) { + mChannel.at(i).second = 0; + return static_cast(i); + } + } + // It is possible to get a note off for a note not playing because the channel was taken for another newer note + return -1; + } + + int8_t GraphInputMidiKeyboard::GetNextNoteChannel(int32_t note) { + for (size_t i = 0; i < mChannel.size(); i++) { + if (mChannel.at(i).first == note) { + mChannel.at(i).second = mNoteCounter++; + return static_cast(i); + } + } + + for (size_t i = 0; i < mChannel.size(); i++) { + if (mChannel.at(i).first == 0) { + mChannel.at(i).first = note; + mChannel.at(i).second = mNoteCounter++; + return static_cast(i); + } + } + + int32_t lowestCount = INT32_MAX; + int8_t lowestCountIndex = 0; + for (size_t i = 0; i < mChannel.size(); i++) { + if (lowestCount > mChannel.at(i).second) { + lowestCount = mChannel.at(i).second; + lowestCountIndex = static_cast(i); + } + } + mChannel.at(lowestCountIndex).first = note; + mChannel.at(lowestCountIndex).second = mNoteCounter++; + return lowestCountIndex; + } + + /*********************************************************************/ + void GraphInputMidiKnobs::Process(int32_t, std::vector& inputs, std::vector& outputs) { + for (size_t i = 0; i < inputs.size(); i++) { + outputs.at(i).mOutput = inputs.at(i).Get(); + } + } + + void GraphInputMidiKnobs::Reset() { + for (int8_t i = 0; i < GetNumInputs(); i++) { + mNode->SetInput(i, 0.0f); + } + } + + void GraphInputMidiKnobs::MidiEvent(const l::hid::midi::MidiData& data) { + if (data.channel != 0) { + return; + } + if (data.status == 11) { + if (data.data1 >= 48 && data.data1 <= 55) { + mNode->SetInput(static_cast(data.data1 - 48), data.data2 / 128.0f); + } + } + } + + /*********************************************************************/ + void GraphInputMidiButtons::Reset() { + for (int8_t i = 0; i < GetNumInputs(); i++) { + mNode->SetInput(i, 0.0f); + } + } + + void GraphInputMidiButtons::Process(int32_t, std::vector& inputs, std::vector& outputs) { + for (size_t i = 0; i < inputs.size(); i++) { + outputs.at(i).mOutput = inputs.at(i).Get(); + } + } + + void GraphInputMidiButtons::Tick(int32_t, float) { + if (mInitCounter == 120){ + for (int8_t i = 0; i < 8; i++) { + if (mButtonStates.at(i) == BUTTON_ALLOCATED) { + // Set to off when allocated and output has been polled, i.e been connected and sampled + UpdateButton(i, BUTTON_OFF); + } + } + } + mInitCounter++; + } + + void GraphInputMidiButtons::MidiEvent(const l::hid::midi::MidiData& data) { + if (data.channel != 0) { + return; + } + if (data.data1 == 98) { + // shift button event + mMidiShiftState = data.status == 9 ? true : data.status == 8 ? false : mMidiShiftState; + } + + int8_t buttonIndex = static_cast(data.data1 - mButtonGroup * 8); + if (buttonIndex >= 0 && buttonIndex < 8) { + // pad button event + if (data.status == 9) { + if (mButtonStates.at(buttonIndex) <= BUTTON_ALLOCATED) { + // if button has not been polled before user input, set button to off first time + mButtonStates.at(buttonIndex) = BUTTON_OFF; + }else if (!mMidiShiftState && mButtonStates.at(buttonIndex) < BUTTON_ON) { + mButtonStates.at(buttonIndex)++; + }else if (mMidiShiftState && mButtonStates.at(buttonIndex) > BUTTON_OFF) { + mButtonStates.at(buttonIndex)--; + } + } + float outputValue = static_cast(mButtonStates.at(buttonIndex) - BUTTON_OFF) / 2.0f; + mNode->SetInput(buttonIndex, outputValue); + UpdateButton(buttonIndex, mButtonStates.at(buttonIndex)); + } + } +} diff --git a/packages/nodegraph/source/common/NodeGraphOpLogic.cpp b/packages/nodegraph/source/common/NodeGraphOpLogic.cpp new file mode 100644 index 00000000..bdbc995d --- /dev/null +++ b/packages/nodegraph/source/common/NodeGraphOpLogic.cpp @@ -0,0 +1,12 @@ +#include "nodegraph/NodeGraphOpLogic.h" + +#include "logging/Log.h" +#include "audio/AudioUtils.h" + +#include "math/MathFunc.h" + +#include + +namespace l::nodegraph { + +} diff --git a/packages/nodegraph/source/common/NodeGraphOpNumeric.cpp b/packages/nodegraph/source/common/NodeGraphOpNumeric.cpp new file mode 100644 index 00000000..5235cb40 --- /dev/null +++ b/packages/nodegraph/source/common/NodeGraphOpNumeric.cpp @@ -0,0 +1,12 @@ +#include "nodegraph/NodeGraphOpNumeric.h" + +#include "logging/Log.h" +#include "audio/AudioUtils.h" + +#include "math/MathFunc.h" + +#include + +namespace l::nodegraph { + +} diff --git a/packages/nodegraph/source/common/NodeGraphOpOutput.cpp b/packages/nodegraph/source/common/NodeGraphOpOutput.cpp new file mode 100644 index 00000000..2a7ae886 --- /dev/null +++ b/packages/nodegraph/source/common/NodeGraphOpOutput.cpp @@ -0,0 +1,129 @@ +#include "nodegraph/NodeGraphOpOutput.h" + +#include "logging/Log.h" +#include "audio/AudioUtils.h" + +#include "math/MathFunc.h" + +#include + +namespace l::nodegraph { + + /*********************************************************************/ + void GraphOutputDebug::Reset() { + mValue = 0.0; + mNode->SetInput(1, 0.5f); + mNode->SetInputBound(1, InputBound::INPUT_0_TO_1); + } + + void GraphOutputDebug::Process(int32_t, 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::Reset() { + mEnvelope = 0.0f; + mNode->SetInput(2, 0.5f); + mNode->SetInputBound(2, InputBound::INPUT_0_TO_1); + } + + void GraphOutputSpeaker::Process(int32_t, std::vector& inputs, std::vector&) { + auto& buffer = mAudioStream->GetWriteBuffer(); + + float in0 = inputs.at(0).Get(); + float in1 = inputs.at(1).Get(); + + float volume = inputs.at(2).Get(); + volume *= volume; + + if (false) { // limiter, causes distorsion + float limit = volume; + volume *= 0.95f; + + float inPreamp0 = in0 * volume; + float inPreamp1 = in1 * volume; + + if (inPreamp0 >= limit || inPreamp0 <= -limit) { + if (inPreamp0 > 0.0f) { + inPreamp0 = limit + (1.0f - limit) * l::math::functions::sigmoidFast((inPreamp0 - limit) / ((1.0f - limit) * 1.5f)); + } + else { + inPreamp0 = -(limit + (1.0f - limit) * l::math::functions::sigmoidFast((-inPreamp0 - limit) / ((1.0f - limit) * 1.5f))); + } + } + if (inPreamp1 >= limit || inPreamp1 <= -limit) { + if (inPreamp1 > 0.0f) { + inPreamp1 = limit + (1.0f - limit) * l::math::functions::sigmoidFast((inPreamp1 - limit) / ((1.0f - limit) * 1.5f)); + } + else { + inPreamp1 = -(limit + (1.0f - limit) * l::math::functions::sigmoidFast((-inPreamp1 - limit) / ((1.0f - limit) * 1.5f))); + } + } + buffer[mCurrentStereoPosition++] = inPreamp0; + buffer[mCurrentStereoPosition++] = inPreamp1; + } + else { + float attackMs = 40.0f; + float releaseMs = 40.0f; + 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 limit = volume; + volume *= 0.95f; + + float inVal0 = in0 * volume; + float inVal1 = in1 * volume; + float inVal = inVal0 > inVal1 ? inVal0 : inVal1; + if (inVal > mEnvelope) { + mEnvelope = attack * (mEnvelope - inVal) + inVal; + } + else { + mEnvelope = release * (mEnvelope - inVal) + inVal; + } + + float out0 = 0.0f; + float out1 = 0.0f; + float envelopeAbs = l::math::functions::abs(mEnvelope); + if (envelopeAbs > limit) { + if (envelopeAbs > 1.0f) { + out0 = inVal0 / mEnvelope; + out1 = inVal1 / mEnvelope; + } + else { + out0 = inVal0 / (1.0f + mEnvelope - limit); + out1 = inVal1 / (1.0f + mEnvelope - limit); + } + } + else { + out0 = inVal0; + out1 = inVal1; + } + + buffer[mCurrentStereoPosition++] = out0; + buffer[mCurrentStereoPosition++] = out1; + } + + mCurrentStereoPosition %= mAudioStream->GetPartTotalSize(); + } + + /*********************************************************************/ + void GraphOutputPlot::Reset() { + mNode->SetInput(0, 0.0f); + mNode->SetInputBound(0, InputBound::INPUT_NEG_1_POS_1); + } + + void GraphOutputPlot::Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) { + float* value = &inputs.at(0).Get(numSamples); + int32_t outputSize = outputs.at(0).GetOutputSize(); + float* output = &outputs.at(0).GetOutput(outputSize); + + for (int32_t i = 0; i < numSamples; i++) { + output[mCurIndex] = *value++; + mCurIndex = (mCurIndex + 1) % outputSize; + } + } + +} diff --git a/packages/nodegraph/source/common/NodeGraphOpSource.cpp b/packages/nodegraph/source/common/NodeGraphOpSource.cpp new file mode 100644 index 00000000..02402316 --- /dev/null +++ b/packages/nodegraph/source/common/NodeGraphOpSource.cpp @@ -0,0 +1,340 @@ +#include "nodegraph/NodeGraphOpSource.h" + +#include "logging/Log.h" +#include "audio/AudioUtils.h" + +#include "math/MathFunc.h" + +#include + +namespace l::nodegraph { + + /* Mathematical operations */ + + /*********************************************************************/ + void GraphSourceConstants::Reset() { + switch (mMode) { + case 0: + for (int8_t i = 0; i < 4; i++) { + mNode->SetInputBound(i, InputBound::INPUT_0_TO_1); + } + break; + case 1: + for (int8_t i = 0; i < 4; i++) { + mNode->SetInputBound(i, InputBound::INPUT_NEG_1_POS_1); + } + break; + case 2: + for (int8_t i = 0; i < 4; i++) { + mNode->SetInputBound(i, InputBound::INPUT_0_100); + } + break; + default: + for (int8_t i = 0; i < 4; i++) { + mNode->SetInputBound(i, InputBound::INPUT_UNBOUNDED); + } + break; + } + } + + void GraphSourceConstants::Process(int32_t, std::vector& inputs, std::vector& outputs) { + for (int8_t i = 0; i < mNumOutputs; i++) { + outputs.at(i).mOutput = inputs.at(i).Get(); + } + } + + void GraphSourceConstants::Tick(int32_t, float) { + mNode->ProcessSubGraph(1); + } + + /*********************************************************************/ + void GraphSourceTime::Process(int32_t, std::vector&, std::vector& outputs) { + float rate = 44100.0f; + float phaseChange = 1.0f / rate; + mAudioTime += phaseChange; + + outputs.at(0).mOutput = mAudioTime; + outputs.at(1).mOutput = mFrameTime; + } + + void GraphSourceTime::Tick(int32_t, float deltaTime) { + mFrameTime += deltaTime; + } + + void GraphSourceTime::Reset() { + mAudioTime = 0.0f; + mFrameTime = 0.0f; + } + + /*********************************************************************/ + void GraphSourceSine::Reset() { + // { "Freq Hz", "Freq Mod", "Phase Mod", "Reset"}; + mPhase = 0.0f; + mNode->SetInput(0, 0.0f); + mNode->SetInput(1, 0.0f); + mNode->SetInput(2, 1.0f); + mNode->SetInput(3, 0.0f); + mNode->SetInput(4, 0.0f); + mNode->SetInputBound(0, InputBound::INPUT_UNBOUNDED); + mNode->SetInputBound(1, InputBound::INPUT_0_100); + mNode->SetInputBound(2, InputBound::INPUT_UNBOUNDED); + mNode->SetInputBound(3, InputBound::INPUT_UNBOUNDED); + mNode->SetInputBound(4, InputBound::INPUT_0_TO_1); + } + + void GraphSourceSine::Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) { + float* output0 = &outputs.at(0).GetOutput(numSamples); + + mSamplesUntilUpdate = l::audio::BatchUpdate(256.0f, mSamplesUntilUpdate, 0, numSamples, + [&]() { + mFreq = l::math::functions::max(static_cast(inputs.at(0).Get()), 0.0); + mVolume = inputs.at(1).Get(); + mReset = inputs.at(4).Get(); + + if (mFreq == 0.0f) { + mVolume = 0.0f; + outputs.at(0).mOutput = 0.0f; + return; + } + if (mReset > 0.5f) { + mVolume = 0.0f; + } + mDeltaTime = 1.0 / 44100.0; + + }, + [&](int32_t start, int32_t end, bool) { + + mFmod = static_cast(inputs.at(2).Get()); + mPmod = static_cast(inputs.at(3).Get()); + + //double limitFmMod = 1.0 / l::math::functions::max(mFreq / 25.0, 1.0); + //mPmod = 800.0 * fmMod * fmMod * limitFmMod; + + double limitFmMod = 1.0 / l::math::functions::max(mFreq / 25.0, 1.0); + mFmod = 800.0 * mFmod * mFmod * limitFmMod; + + for (int32_t i = start; i < end; i++) { + double phaseDelta = mDeltaTime * mFreq; + + mPhase += phaseDelta * (1.0 + mFmod); + mPhase = l::math::functions::mod(mPhase, 1.0); + + mPhaseMod += mPhase + mPmod; + mPhaseMod = l::math::functions::mod(mPhaseMod, 1.0); + + double sine = l::math::functions::sin(l::math::constants::PI * (mPhase + mPhaseMod)); + + mVol += (1.0f / 256.0f) * (mVolume - mVol); + + *output0++ = mVol * static_cast(sine); + } + } + ); + } + + /*********************************************************************/ + void GraphSourceSineFM::Reset() { + // { "Note", "Volume", "Fmod", "FmodFreq", "FmodVol", "FmodOfs", "Reset"} + mPhase = 0.0f; + mNode->SetInput(0, 0.0f); + mNode->SetInput(1, 0.5f); + mNode->SetInput(2, 0.0f); + mNode->SetInput(3, 0.0f); + mNode->SetInput(4, 0.0f); + mNode->SetInput(5, 0.0f); + mNode->SetInput(6, 0.0f); + mNode->SetInput(7, 0.0f); + mNode->SetInputBound(0, InputBound::INPUT_UNBOUNDED); + mNode->SetInputBound(1, InputBound::INPUT_0_100); + mNode->SetInputBound(2, InputBound::INPUT_UNBOUNDED); + mNode->SetInputBound(3, InputBound::INPUT_UNBOUNDED); + mNode->SetInputBound(4, InputBound::INPUT_UNBOUNDED); + mNode->SetInputBound(5, InputBound::INPUT_UNBOUNDED); + mNode->SetInputBound(6, InputBound::INPUT_0_TO_1); + mNode->SetInputBound(7, InputBound::INPUT_0_TO_1); + } + + void GraphSourceSineFM::Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) { + float* output0 = &outputs.at(0).GetOutput(numSamples); + + mSamplesUntilUpdate = l::audio::BatchUpdate(256.0f, mSamplesUntilUpdate, 0, numSamples, + [&]() { + mFreq = l::math::functions::max(static_cast(inputs.at(0).Get()), 0.0); + mVolume = inputs.at(1).Get(); + mReset = inputs.at(7).Get(); + + if (mReset > 0.0f || mVolume + mVol < 0.0000001f) { + mPhase = 0.0; + mPhaseFmod = 0.0; + mVolume = 0.0f; + outputs.at(0).mOutput = 0.0f; + return; + } + mDeltaTime = 1.0 / 44100.0; + + }, + [&](int32_t start, int32_t end, bool) { + + mFmod = static_cast(inputs.at(2).Get()); + mFmodFrq = static_cast(inputs.at(3).Get()); + mFmodVol = static_cast(inputs.at(4).Get()); + mFmodOfs = static_cast(inputs.at(5).Get()); + double fmodGain = static_cast(inputs.at(6).Get()); + + for (int32_t i = start; i < end; i++) { + double phaseDelta2 = mDeltaTime * mFreq * mFmodFrq; + mPhaseFmod += phaseDelta2; + mPhaseFmod = l::math::functions::mod(mPhaseFmod, 1.0); + double modWave = fmodGain * l::math::functions::sin(l::math::constants::PI * mPhaseFmod * 0.5); + double fmod = (mFmodOfs + 1.0) * mFmodVol + mFmodVol * modWave; + fmod = l::math::functions::clamp(fmod, 0.0, 500.0); + + double phaseDelta = mDeltaTime * mFreq * (fmod + 1.0) * (mFmod + 1.0); + phaseDelta = l::math::functions::clamp(phaseDelta, phaseDelta2, 0.5); + mPhase += phaseDelta; + mPhase = l::math::functions::mod(mPhase, 1.0); + double waveTarget = l::math::functions::sin(l::math::constants::PI * mPhase * 2.0); + + mVol += (1.0f / 256.0f) * (mVolume - mVol); + mWave += (waveTarget - mWave) * 0.5; + + *output0++ = mVol * static_cast(mWave); + } + } + ); + } + + /*********************************************************************/ + void GraphSourceSineFM2::Reset() { + // { "Note", "Volume", "FmodVol", "FmodOfs", "Reset"} + mPhase = 0.0f; + mNode->SetInput(0, 0.0f); + mNode->SetInput(1, 0.5f); + mNode->SetInput(2, 0.0f); + mNode->SetInput(3, 0.0f); + mNode->SetInput(4, 0.0f); + mNode->SetInputBound(0, InputBound::INPUT_UNBOUNDED); + mNode->SetInputBound(1, InputBound::INPUT_0_100); + mNode->SetInputBound(2, InputBound::INPUT_0_TO_1); + mNode->SetInputBound(3, InputBound::INPUT_0_100); + mNode->SetInputBound(4, InputBound::INPUT_0_TO_1); + } + + void GraphSourceSineFM2::Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) { + float* output0 = &outputs.at(0).GetOutput(numSamples); + + mSamplesUntilUpdate = l::audio::BatchUpdate(256.0f, mSamplesUntilUpdate, 0, numSamples, + [&]() { + mFreq = l::math::functions::max(static_cast(inputs.at(0).Get()), 0.0); + mVolume = inputs.at(1).Get(); + mReset = inputs.at(4).Get(); + + if (mReset > 0.0f || mVolume + mVol < 0.0000001f) { + mPhase = 0.0; + mPhaseFmod = 0.0; + mVolume = 0.0f; + outputs.at(0).mOutput = 0.0f; + return; + } + mDeltaTime = 1.0 / 44100.0; + mDeltaLimit = mDeltaTime * 2.0; + + }, + [&](int32_t start, int32_t end, bool) { + + double fmMod = static_cast(inputs.at(2).Get()); + double fmFreq = static_cast(inputs.at(3).Get()); + + double limitFmMod = 1.0 / l::math::functions::max(mFreq / 25.0, 1.0); + fmMod = 800.0 * fmMod * fmMod * limitFmMod; + + for (int32_t i = start; i < end; i++) { + double maxFmModulation = 1.0 / l::math::functions::max(mFreq * fmFreq * mDeltaLimit, 1.0); + maxFmModulation *= maxFmModulation; + maxFmModulation *= maxFmModulation; + fmFreq = fmFreq * maxFmModulation; + + double fmNote = mFreq * fmFreq; + double phaseDelta2 = mDeltaTime * fmNote; + mPhaseFmod += phaseDelta2; + mPhaseFmod = l::math::functions::mod(mPhaseFmod, 1.0); + mFmod = (fmMod + fmMod * l::math::functions::sin(l::math::constants::PI * mPhaseFmod * 2.0)); + + double phaseDelta = mDeltaTime * mFreq * (mFmod + 1.0) / (fmMod + 1.0); + + mPhase += phaseDelta; + mPhase = l::math::functions::mod(mPhase, 1.0); + double waveTarget = l::math::functions::sin(l::math::constants::PI * mPhase * 2.0); + + + mVol += (1.0f / 256.0f) * (mVolume - mVol); + mWave += (waveTarget - mWave) * 0.5; + + *output0++ = mVol * static_cast(mWave); + } + } + ); + } + + /*********************************************************************/ + void GraphSourceSineFM3::Reset() { + // { "Note", "Volume", "Fmod", "Reset"} + mPhase = 0.0f; + mNode->SetInput(0, 0.0f); + mNode->SetInput(1, 0.5f); + mNode->SetInput(2, 0.0f); + mNode->SetInput(3, 0.0f); + mNode->SetInputBound(0, InputBound::INPUT_UNBOUNDED); + mNode->SetInputBound(1, InputBound::INPUT_0_100); + mNode->SetInputBound(2, InputBound::INPUT_0_TO_1); + mNode->SetInputBound(3, InputBound::INPUT_0_TO_1); + } + + void GraphSourceSineFM3::Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) { + float* output0 = &outputs.at(0).GetOutput(numSamples); + + mSamplesUntilUpdate = l::audio::BatchUpdate(16.0f, mSamplesUntilUpdate, 0, numSamples, + [&]() { + mFreq = l::math::functions::max(static_cast(inputs.at(0).Get()), 0.0); + mVolume = inputs.at(1).Get(); + mReset = inputs.at(3).Get(); + + if (mReset > 0.0f || mVolume < 0.0000001f) { + mPhase = 0.0; + mPhaseFmod = 0.0; + mVolume = 0.0f; + outputs.at(0).mOutput = 0.0f; + return; + } + mDeltaTime = 1.0 / 44100.0; + mDeltaLimit = mDeltaTime * 4.0; + }, + [&](int32_t start, int32_t end, bool) { + + double fmMod = static_cast(inputs.at(2).Get()); + + double limitFmMod = 1.0 / l::math::functions::max(mFreq / 25.0, 1.0); + double fm = 800.0 * fmMod * fmMod * limitFmMod; + + for (int32_t i = start; i < end; i++) { + double phaseDelta2 = mDeltaTime * mFreq; + mPhaseFmod += phaseDelta2; + mPhaseFmod = l::math::functions::mod(mPhaseFmod, 1.0); + double modulation = fm * l::math::functions::sin(l::math::constants::PI * mPhaseFmod * 2.0); + + double phaseDelta = mDeltaTime * mFreq * modulation; + mPhase += phaseDelta; + mPhase = l::math::functions::mod(mPhase, 1.0); + double waveTarget = l::math::functions::sin(l::math::constants::PI * mPhase * 2.0); + + + mVol += (1.0f / 256.0f) * (mVolume - mVol); + mWave += (waveTarget - mWave) * 0.5; + + *output0++ = mVol * static_cast(mWave); + } + } + ); + } + +} diff --git a/packages/nodegraph/source/common/NodeGraphOperations.cpp b/packages/nodegraph/source/common/NodeGraphOperations.cpp deleted file mode 100644 index 914a5d2a..00000000 --- a/packages/nodegraph/source/common/NodeGraphOperations.cpp +++ /dev/null @@ -1,1169 +0,0 @@ -#include "nodegraph/NodeGraphOperations.h" - -#include "logging/Log.h" -#include "audio/AudioUtils.h" - -#include "math/MathFunc.h" - -#include - -namespace l::nodegraph { - - /* Mathematical operations */ - - /*********************************************************************/ - void GraphSourceConstants::Reset() { - switch (mMode) { - case 0: - for (int8_t i = 0; i < 4; i++) { - mNode->SetInputBound(i, InputBound::INPUT_0_TO_1); - } - break; - case 1: - for (int8_t i = 0; i < 4; i++) { - mNode->SetInputBound(i, InputBound::INPUT_NEG_1_POS_1); - } - break; - case 2: - for (int8_t i = 0; i < 4; i++) { - mNode->SetInputBound(i, InputBound::INPUT_0_100); - } - break; - default: - for (int8_t i = 0; i < 4; i++) { - mNode->SetInputBound(i, InputBound::INPUT_UNBOUNDED); - } - break; - } - } - - void GraphSourceConstants::Process(int32_t, std::vector& inputs, std::vector& outputs) { - for (int8_t i = 0; i < mNumOutputs; i++) { - outputs.at(i).mOutput = inputs.at(i).Get(); - } - } - - void GraphSourceConstants::Tick(int32_t, float) { - mNode->ProcessSubGraph(1); - } - - /*********************************************************************/ - void GraphSourceTime::Process(int32_t, std::vector&, std::vector& outputs) { - float rate = 44100.0f; - float phaseChange = 1.0f / rate; - mAudioTime += phaseChange; - - outputs.at(0).mOutput = mAudioTime; - outputs.at(1).mOutput = mFrameTime; - } - - void GraphSourceTime::Tick(int32_t, float deltaTime) { - mFrameTime += deltaTime; - } - - void GraphSourceTime::Reset() { - mAudioTime = 0.0f; - mFrameTime = 0.0f; - } - - /*********************************************************************/ - void GraphSourceSine::Reset() { - // { "Freq Hz", "Freq Mod", "Phase Mod", "Reset"}; - mPhase = 0.0f; - mNode->SetInput(0, 0.0f); - mNode->SetInput(1, 0.0f); - mNode->SetInput(2, 1.0f); - mNode->SetInput(3, 0.0f); - mNode->SetInput(4, 0.0f); - mNode->SetInputBound(0, InputBound::INPUT_UNBOUNDED); - mNode->SetInputBound(1, InputBound::INPUT_0_100); - mNode->SetInputBound(2, InputBound::INPUT_UNBOUNDED); - mNode->SetInputBound(3, InputBound::INPUT_UNBOUNDED); - mNode->SetInputBound(4, InputBound::INPUT_0_TO_1); - } - - void GraphSourceSine::Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) { - float* output0 = &outputs.at(0).GetOutput(numSamples); - - mSamplesUntilUpdate = l::audio::BatchUpdate(256.0f, mSamplesUntilUpdate, 0, numSamples, - [&]() { - mNote = l::math::functions::max(static_cast(inputs.at(0).Get()), 0.0); - mVolume = inputs.at(1).Get(); - mReset = inputs.at(4).Get(); - - if (mNote == 0.0f) { - mVolume = 0.0f; - outputs.at(0).mOutput = 0.0f; - return; - } - if (mReset > 0.5f) { - mVolume = 0.0f; - } - mDeltaTime = 1.0 / 44100.0; - - }, - [&](int32_t start, int32_t end, bool) { - - mFmod = static_cast(inputs.at(2).Get()); - mPmod = static_cast(inputs.at(3).Get()); - - //double limitFmMod = 1.0 / l::math::functions::max(mNote / 25.0, 1.0); - //mPmod = 800.0 * fmMod * fmMod * limitFmMod; - - double limitFmMod = 1.0 / l::math::functions::max(mNote / 25.0, 1.0); - mFmod = 800.0 * mFmod * mFmod * limitFmMod; - - for (int32_t i = start; i < end; i++) { - double phaseDelta = mDeltaTime * mNote; - - mPhase += phaseDelta * (1.0 + mFmod); - mPhase = l::math::functions::mod(mPhase, 1.0); - - mPhaseMod += mPhase + mPmod; - mPhaseMod = l::math::functions::mod(mPhaseMod, 1.0); - - double sine = l::math::functions::sin(l::math::constants::PI * (mPhase + mPhaseMod)); - - mVol += (1.0f / 256.0f) * (mVolume - mVol); - - *output0++ = mVol * static_cast(sine); - } - } - ); - } - - /*********************************************************************/ - void GraphSourceSineFM::Reset() { - // { "Note", "Volume", "Fmod", "FmodFreq", "FmodVol", "FmodOfs", "Reset"} - mPhase = 0.0f; - mNode->SetInput(0, 0.0f); - mNode->SetInput(1, 0.5f); - mNode->SetInput(2, 0.0f); - mNode->SetInput(3, 0.0f); - mNode->SetInput(4, 0.0f); - mNode->SetInput(5, 0.0f); - mNode->SetInput(6, 0.0f); - mNode->SetInput(7, 0.0f); - mNode->SetInputBound(0, InputBound::INPUT_UNBOUNDED); - mNode->SetInputBound(1, InputBound::INPUT_0_100); - mNode->SetInputBound(2, InputBound::INPUT_UNBOUNDED); - mNode->SetInputBound(3, InputBound::INPUT_UNBOUNDED); - mNode->SetInputBound(4, InputBound::INPUT_UNBOUNDED); - mNode->SetInputBound(5, InputBound::INPUT_UNBOUNDED); - mNode->SetInputBound(6, InputBound::INPUT_0_TO_1); - mNode->SetInputBound(7, InputBound::INPUT_0_TO_1); - } - - void GraphSourceSineFM::Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) { - float* output0 = &outputs.at(0).GetOutput(numSamples); - - mSamplesUntilUpdate = l::audio::BatchUpdate(256.0f, mSamplesUntilUpdate, 0, numSamples, - [&]() { - mNote = l::math::functions::max(static_cast(inputs.at(0).Get()), 0.0); - mVolume = inputs.at(1).Get(); - mReset = inputs.at(7).Get(); - - if (mReset > 0.0f || mVolume + mVol < 0.0000001f) { - mPhase = 0.0; - mPhaseFmod = 0.0; - mVolume = 0.0f; - outputs.at(0).mOutput = 0.0f; - return; - } - mDeltaTime = 1.0 / 44100.0; - - }, - [&](int32_t start, int32_t end, bool) { - - mFmod = static_cast(inputs.at(2).Get()); - mFmodFrq = static_cast(inputs.at(3).Get()); - mFmodVol = static_cast(inputs.at(4).Get()); - mFmodOfs = static_cast(inputs.at(5).Get()); - double fmodGain = static_cast(inputs.at(6).Get()); - - for (int32_t i = start; i < end; i++) { - double phaseDelta2 = mDeltaTime * mNote * mFmodFrq; - mPhaseFmod += phaseDelta2; - mPhaseFmod = l::math::functions::mod(mPhaseFmod, 1.0); - double modWave = fmodGain * l::math::functions::sin(l::math::constants::PI * mPhaseFmod * 0.5); - double fmod = (mFmodOfs + 1.0) * mFmodVol + mFmodVol * modWave; - fmod = l::math::functions::clamp(fmod, 0.0, 500.0); - - double phaseDelta = mDeltaTime * mNote * (fmod + 1.0) * (mFmod + 1.0); - phaseDelta = l::math::functions::clamp(phaseDelta, phaseDelta2, 0.5); - mPhase += phaseDelta; - mPhase = l::math::functions::mod(mPhase, 1.0); - double waveTarget = l::math::functions::sin(l::math::constants::PI * mPhase * 2.0); - - mVol += (1.0f / 256.0f) * (mVolume - mVol); - mWave += (waveTarget - mWave) * 0.5; - - *output0++ = mVol * static_cast(mWave); - } - } - ); - } - - /*********************************************************************/ - void GraphSourceSineFM2::Reset() { - // { "Note", "Volume", "FmodVol", "FmodOfs", "Reset"} - mPhase = 0.0f; - mNode->SetInput(0, 0.0f); - mNode->SetInput(1, 0.5f); - mNode->SetInput(2, 0.0f); - mNode->SetInput(3, 0.0f); - mNode->SetInput(4, 0.0f); - mNode->SetInputBound(0, InputBound::INPUT_UNBOUNDED); - mNode->SetInputBound(1, InputBound::INPUT_0_100); - mNode->SetInputBound(2, InputBound::INPUT_0_TO_1); - mNode->SetInputBound(3, InputBound::INPUT_0_100); - mNode->SetInputBound(4, InputBound::INPUT_0_TO_1); - } - - void GraphSourceSineFM2::Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) { - float* output0 = &outputs.at(0).GetOutput(numSamples); - - mSamplesUntilUpdate = l::audio::BatchUpdate(256.0f, mSamplesUntilUpdate, 0, numSamples, - [&]() { - mNote = l::math::functions::max(static_cast(inputs.at(0).Get()), 0.0); - mVolume = inputs.at(1).Get(); - mReset = inputs.at(4).Get(); - - if (mReset > 0.0f || mVolume + mVol < 0.0000001f) { - mPhase = 0.0; - mPhaseFmod = 0.0; - mVolume = 0.0f; - outputs.at(0).mOutput = 0.0f; - return; - } - mDeltaTime = 1.0 / 44100.0; - mDeltaLimit = mDeltaTime * 2.0; - - }, - [&](int32_t start, int32_t end, bool) { - - double fmMod = static_cast(inputs.at(2).Get()); - double fmFreq = static_cast(inputs.at(3).Get()); - - double limitFmMod = 1.0 / l::math::functions::max(mNote / 25.0, 1.0); - fmMod = 800.0 * fmMod * fmMod * limitFmMod; - - for (int32_t i = start; i < end; i++) { - double maxFmModulation = 1.0 / l::math::functions::max(mNote * fmFreq * mDeltaLimit, 1.0); - maxFmModulation *= maxFmModulation; - maxFmModulation *= maxFmModulation; - fmFreq = fmFreq * maxFmModulation; - - double fmNote = mNote * fmFreq; - double phaseDelta2 = mDeltaTime * fmNote; - mPhaseFmod += phaseDelta2; - mPhaseFmod = l::math::functions::mod(mPhaseFmod, 1.0); - mFmod = (fmMod + fmMod * l::math::functions::sin(l::math::constants::PI * mPhaseFmod * 2.0)); - - double phaseDelta = mDeltaTime * mNote * (mFmod + 1.0) / (fmMod + 1.0); - - mPhase += phaseDelta; - mPhase = l::math::functions::mod(mPhase, 1.0); - double waveTarget = l::math::functions::sin(l::math::constants::PI * mPhase * 2.0); - - - mVol += (1.0f / 256.0f) * (mVolume - mVol); - mWave += (waveTarget - mWave) * 0.5; - - *output0++ = mVol * static_cast(mWave); - } - } - ); - } - - /*********************************************************************/ - void GraphSourceSineFM3::Reset() { - // { "Note", "Volume", "Fmod", "Reset"} - mPhase = 0.0f; - mNode->SetInput(0, 0.0f); - mNode->SetInput(1, 0.5f); - mNode->SetInput(2, 0.0f); - mNode->SetInput(3, 0.0f); - mNode->SetInputBound(0, InputBound::INPUT_UNBOUNDED); - mNode->SetInputBound(1, InputBound::INPUT_0_100); - mNode->SetInputBound(2, InputBound::INPUT_0_TO_1); - mNode->SetInputBound(3, InputBound::INPUT_0_TO_1); - } - - void GraphSourceSineFM3::Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) { - float* output0 = &outputs.at(0).GetOutput(numSamples); - - mSamplesUntilUpdate = l::audio::BatchUpdate(16.0f, mSamplesUntilUpdate, 0, numSamples, - [&]() { - mNote = l::math::functions::max(static_cast(inputs.at(0).Get()), 0.0); - mVolume = inputs.at(1).Get(); - mReset = inputs.at(3).Get(); - - if (mReset > 0.0f || mVolume < 0.0000001f) { - mPhase = 0.0; - mPhaseFmod = 0.0; - mVolume = 0.0f; - outputs.at(0).mOutput = 0.0f; - return; - } - mDeltaTime = 1.0 / 44100.0; - mDeltaLimit = mDeltaTime * 4.0; - }, - [&](int32_t start, int32_t end, bool) { - - double fmMod = static_cast(inputs.at(2).Get()); - - double limitFmMod = 1.0 / l::math::functions::max(mNote / 25.0, 1.0); - double fm = 800.0 * fmMod * fmMod * limitFmMod; - - for (int32_t i = start; i < end; i++) { - double phaseDelta2 = mDeltaTime * mNote; - mPhaseFmod += phaseDelta2; - mPhaseFmod = l::math::functions::mod(mPhaseFmod, 1.0); - double modulation = fm * l::math::functions::sin(l::math::constants::PI * mPhaseFmod * 2.0); - - double phaseDelta = mDeltaTime * mNote * modulation; - mPhase += phaseDelta; - mPhase = l::math::functions::mod(mPhase, 1.0); - double waveTarget = l::math::functions::sin(l::math::constants::PI * mPhase * 2.0); - - - mVol += (1.0f / 256.0f) * (mVolume - mVol); - mWave += (waveTarget - mWave) * 0.5; - - *output0++ = mVol * static_cast(mWave); - } - } - ); - } - - /* Stateful filtering operations */ - - /*********************************************************************/ - void GraphFilterLowpass::Reset() { - mState0 = 0.0f; - mState1 = 0.0f; - mNode->SetInput(1, 0.99f); - mNode->SetInput(2, 0.01f); - mNode->SetInputBound(1, InputBound::INPUT_0_TO_1); - mNode->SetInputBound(2, InputBound::INPUT_0_TO_1); - } - - void GraphFilterLowpass::Process(int32_t, std::vector& inputs, std::vector& outputs) { - float inputValue = inputs.at(0).Get(); - float cutoff = inputs.at(1).Get(); - float resonance = 1.0f - inputs.at(2).Get(); - - cutoff *= cutoff; - float rc = 1.0f - resonance * cutoff; - - mState0 = rc * mState0 - cutoff * (mState1 + inputValue); - mState1 = rc * mState1 + cutoff * mState0; - - outputs.at(0).mOutput = -mState1; - } - - /*********************************************************************/ - void GraphFilterEnvelope::Reset() { - mEnvelope = 0.0f; - mNode->SetInput(1, 0.5f); - mNode->SetInput(2, 50.0f); - mNode->SetInput(3, 50.0f); - mNode->SetInput(4, 0.1f); - mNode->SetInputBound(1, InputBound::INPUT_0_TO_1); - mNode->SetInputBound(2, InputBound::INPUT_CUSTOM, 1.0f, 100000.0f); - mNode->SetInputBound(3, InputBound::INPUT_CUSTOM, 1.0f, 100000.0f); - mNode->SetInputBound(4, InputBound::INPUT_CUSTOM, 0.0001f, 1.0f); - } - - void GraphFilterEnvelope::Process(int32_t, std::vector& inputs, std::vector& outputs) { - float noteTarget = inputs.at(0).Get(); - float velocity = l::math::functions::pow(inputs.at(1).Get(), 0.5f); - float attackFrames = inputs.at(2).Get() * 44100.0f / 1000.0f; - float releaseFrames = inputs.at(3).Get() * 44100.0f / 1000.0f; - float noteFade = inputs.at(4).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) { - mNote = noteTarget; - } - // attack - mNote = noteTarget; - mFrameCount++; - } - - if (noteTarget != 0.0f) { - if (mEnvelopeTarget < velocity) { - mEnvelopeTarget += velocity / attackFrames; - } - else { - mEnvelopeTarget = velocity; - } - mNote += noteFade * noteFade * (noteTarget - mNote); - //mNote = noteFade * (mEnvelope - noteTarget) + noteTarget; - } - else { - // release - if (mFrameCount > 0) { - mEnvelopeTarget -= velocity / releaseFrames; - if (mEnvelopeTarget < 0.0f) { - mEnvelopeTarget = 0.0f; - mFrameCount = 0; - } - } - else { - mEnvelopeTarget = 0.0f; - mFrameCount = 0; - } - } - - mEnvelope += 0.1f * (mEnvelopeTarget - mEnvelope); - outputs.at(0).mOutput = mNote; - 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(int32_t, 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::Reset() { - mEnvelope = 0.0f; - mNode->SetInput(2, 0.5f); - mNode->SetInputBound(2, InputBound::INPUT_0_TO_1); - } - - void GraphOutputSpeaker::Process(int32_t, std::vector& inputs, std::vector&) { - auto& buffer = mAudioStream->GetWriteBuffer(); - - float in0 = inputs.at(0).Get(); - float in1 = inputs.at(1).Get(); - - float volume = inputs.at(2).Get(); - volume *= volume; - - if (false) { // limiter, causes distorsion - float limit = volume; - volume *= 0.95f; - - float inPreamp0 = in0 * volume; - float inPreamp1 = in1 * volume; - - if (inPreamp0 >= limit || inPreamp0 <= -limit) { - if (inPreamp0 > 0.0f) { - inPreamp0 = limit + (1.0f - limit) * l::math::functions::sigmoidFast((inPreamp0 - limit) / ((1.0f - limit) * 1.5f)); - } - else { - inPreamp0 = -(limit + (1.0f - limit) * l::math::functions::sigmoidFast((-inPreamp0 - limit) / ((1.0f - limit) * 1.5f))); - } - } - if (inPreamp1 >= limit || inPreamp1 <= -limit) { - if (inPreamp1 > 0.0f) { - inPreamp1 = limit + (1.0f - limit) * l::math::functions::sigmoidFast((inPreamp1 - limit) / ((1.0f - limit) * 1.5f)); - } - else { - inPreamp1 = -(limit + (1.0f - limit) * l::math::functions::sigmoidFast((-inPreamp1 - limit) / ((1.0f - limit) * 1.5f))); - } - } - buffer[mCurrentStereoPosition++] = inPreamp0; - buffer[mCurrentStereoPosition++] = inPreamp1; - } - else { - float attackMs = 40.0f; - float releaseMs = 40.0f; - 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 limit = volume; - volume *= 0.95f; - - float inVal0 = in0 * volume; - float inVal1 = in1 * volume; - float inVal = inVal0 > inVal1 ? inVal0 : inVal1; - if (inVal > mEnvelope) { - mEnvelope = attack * (mEnvelope - inVal) + inVal; - } - else { - mEnvelope = release * (mEnvelope - inVal) + inVal; - } - - float out0 = 0.0f; - float out1 = 0.0f; - float envelopeAbs = l::math::functions::abs(mEnvelope); - if (envelopeAbs > limit) { - if (envelopeAbs > 1.0f) { - out0 = inVal0 / mEnvelope; - out1 = inVal1 / mEnvelope; - } - else { - out0 = inVal0 / (1.0f + mEnvelope - limit); - out1 = inVal1 / (1.0f + mEnvelope - limit); - } - } - else { - out0 = inVal0; - out1 = inVal1; - } - - buffer[mCurrentStereoPosition++] = out0; - buffer[mCurrentStereoPosition++] = out1; - } - - mCurrentStereoPosition %= mAudioStream->GetPartTotalSize(); - } - - /*********************************************************************/ - void GraphOutputPlot::Reset() { - mNode->SetInput(0, 0.0f); - mNode->SetInputBound(0, InputBound::INPUT_NEG_1_POS_1); - } - - void GraphOutputPlot::Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) { - float* value = &inputs.at(0).Get(numSamples); - int32_t outputSize = outputs.at(0).GetOutputSize(); - float* output = &outputs.at(0).GetOutput(outputSize); - - for (int32_t i = 0; i < numSamples; i++) { - output[mCurIndex] = *value++; - mCurIndex = (mCurIndex + 1) % outputSize; - } - } - - /*********************************************************************/ - void GraphEffectReverb1::Reset() { - // { "In 1", "In 2", "Mix", "Attenuation", "Room Size", "Delay 1", "Feedback 1", "Delay 2", "Feedback 2", "Delay 3", "Feedback 3" }; - - mNode->SetInput(2, 0.75f); - mNode->SetInput(3, 0.5f); - mNode->SetInput(4, 30.0f); - mNode->SetInput(5, 0.5f); - mNode->SetInput(6, 0.9f); - mNode->SetInput(7, 0.8f); - mNode->SetInput(8, 0.9f); - mNode->SetInput(9, 0.7f); - mNode->SetInput(10, 0.9f); - mNode->SetInputBound(2, InputBound::INPUT_0_TO_1); - mNode->SetInputBound(3, InputBound::INPUT_0_TO_1); - mNode->SetInputBound(4, InputBound::INPUT_CUSTOM, 0.2f, maxRoomSizeInMeters); - mNode->SetInputBound(5, InputBound::INPUT_0_TO_1); - mNode->SetInputBound(6, InputBound::INPUT_0_TO_1); - mNode->SetInputBound(7, InputBound::INPUT_0_TO_1); - mNode->SetInputBound(8, InputBound::INPUT_0_TO_1); - mNode->SetInputBound(9, InputBound::INPUT_0_TO_1); - mNode->SetInputBound(10, InputBound::INPUT_0_TO_1); - } - - void GraphEffectReverb1::Process(int32_t, std::vector&inputs, std::vector&outputs) { - float wet = inputs.at(2).Get(); - - fb = 0.2f * (1.0f - inputs.at(3).Get()); - - float roomSize = inputs.at(4).Get(); - uint32_t bufSizeLimit = GetFramesPerRoomSize(roomSize); - - d0 = inputs.at(5).Get(); - fb0 = 0.5f * 0.5f * math::functions::max(inputs.at(6).Get(), 1.0f); - d1 = inputs.at(7).Get(); - fb1 = 0.5f * 0.5f * math::functions::max(inputs.at(8).Get(), 1.0f); - d2 = inputs.at(9).Get(); - fb2 = 0.5f * 0.5f * math::functions::max(inputs.at(10).Get(), 1.0f); - - float dry = 1.0f - wet; - - uint32_t delay0 = (int(bufIndex + d0 * bufSizeLimit)) % bufSizeLimit; - uint32_t delay1 = (int(bufIndex + d1 * bufSizeLimit)) % bufSizeLimit; - uint32_t delay2 = (int(bufIndex + d2 * bufSizeLimit)) % bufSizeLimit; - float in0 = inputs.at(0).Get(); - float in1 = inputs.at(1).Get(); - - outputs[0].mOutput = in0 * dry + (fb1 * buf1[delay1] + fb0 * buf0[delay0] + fb2 * buf0[delay2]) * wet; - outputs[1].mOutput = in1 * dry + (fb1 * buf0[delay1] + fb0 * buf1[delay0] + fb2 * buf1[delay2]) * wet; - - buf0[bufIndex] = fb * buf1[bufIndex] - fb1 * buf1[delay1] - fb0 * buf0[delay0] - fb2 * buf0[delay2] + in0; - buf1[bufIndex] = fb * buf0[bufIndex] - fb1 * buf0[delay1] - fb0 * buf1[delay0] - fb2 * buf1[delay2] + in1; - - bufIndex = (bufIndex + 1) % bufSizeLimit; - - delay0 = (delay0 + 1) % bufSizeLimit; - delay1 = (delay1 + 1) % bufSizeLimit; - delay2 = (delay2 + 1) % bufSizeLimit; - } - - /*********************************************************************/ - - 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); - mNode->SetInput(5, 0.5f); - mNode->SetInput(6, 0.1f); - mNode->SetInput(7, 0.8f); - mNode->SetInput(8, 5.0f); - mNode->SetInput(9, 0.7f); - mNode->SetInput(10, 0.95f); - mNode->SetInput(11, 0.01f); - mNode->SetInputBound(2, InputBound::INPUT_0_TO_1); - mNode->SetInputBound(3, InputBound::INPUT_0_TO_1); - mNode->SetInputBound(4, InputBound::INPUT_CUSTOM, 1.0f, 334.0f); - mNode->SetInputBound(5, InputBound::INPUT_0_TO_1); - mNode->SetInputBound(6, InputBound::INPUT_CUSTOM, 0.0f, 10.0f); - mNode->SetInputBound(7, InputBound::INPUT_CUSTOM, 0.0f, 10.0f); - mNode->SetInputBound(8, InputBound::INPUT_CUSTOM, 1.0f, 30.0f); - mNode->SetInputBound(9, InputBound::INPUT_CUSTOM, 1.0f, 10.0f); - mNode->SetInputBound(10, InputBound::INPUT_CUSTOM, 0.001f, 0.999f); - mNode->SetInputBound(11, InputBound::INPUT_CUSTOM, 0.001f, 0.999f); - } - - void GraphEffectReverb2::Process(int32_t, std::vector& inputs, std::vector& outputs) { - float wet = inputs.at(2).Get(); - float reverbFeedback = inputs.at(3).Get(); - float roomSize = inputs.at(4).Get(); - float stereoWidth = inputs.at(5).Get(); - float earliestDelay = inputs.at(6).Get(); - float longestDelay = inputs.at(7).Get(); - float numDelays = inputs.at(8).Get(); - float tapBulge = inputs.at(9).Get(); - float cutoff = inputs.at(10).Get(); - float res = inputs.at(11).Get(); - - if (roomSize > maxRoomSizeInMeters) { - roomSize = maxRoomSizeInMeters; - mNode->SetInput(3, maxRoomSizeInMeters); - } - else if (roomSize < 0.1f) { - roomSize = 0.1f; - mNode->SetInput(3, 0.1f); - } - uint32_t bufSizeLimit = GetFramesPerRoomSize(roomSize); - - // feedback and delay - - float dry = 1.0f - wet; - - // indexes for delays with at least one index back - float in0 = inputs.at(0).Get(); - float in1 = inputs.at(1).Get(); - - auto echoStrength = [](float x, float earlyBulgeStrength) { - // f(x) = E * 2 * x * pow(E, -x * 2); - return 2.72f * x * earlyBulgeStrength * powf(2.72f, -x * earlyBulgeStrength); - }; - - auto sample = [&](float* buf0, float* buf1, float earliestDelay, float longestDelay, float numDelays, float attenuation, float stereoWidth) { - float delayLength = longestDelay - earliestDelay; - float delayValue0 = 0.0f; - float delayValue1 = 0.0f; - - float attenuationSum = 0.01f; - float stereoMixing = stereoWidth; - for (int i = 0; i < static_cast(numDelays); i++) { - //float random = mLCG() / static_cast(INT32_MAX); - - float x = 0.5f / numDelays + i / numDelays; - float delay0 = earliestDelay + delayLength * x * x; - float delay1 = delay0; - - uint32_t delayIndex0 = (int(bufIndex + 2 * bufSizeLimit - 1 - delay0 * bufSizeLimit)) % bufSizeLimit; - uint32_t delayIndex1 = (int(bufIndex + 2 * bufSizeLimit - 1 - delay1 * bufSizeLimit)) % bufSizeLimit; - - float monoMixing = 1.0f - stereoMixing; - float gain = echoStrength(x, tapBulge); - delayValue0 += gain * (stereoMixing * buf0[delayIndex0] + monoMixing * buf1[delayIndex0]); - delayValue1 += gain * (stereoMixing * buf1[delayIndex1] + monoMixing * buf0[delayIndex1]); - - attenuationSum += gain; - } - - float gainAdjust = attenuation / attenuationSum; - - // normalize reverb based on input attenuation value - delayValue0 *= gainAdjust; - delayValue1 *= gainAdjust; - - return std::tuple(delayValue0, delayValue1); - }; - - - auto [delay0, delay1] = sample(bufEarlyTap0.data(), bufEarlyTap1.data(), earliestDelay, longestDelay, numDelays, reverbFeedback, stereoWidth); - - bufEarlyTap0[bufTapIndex] = reverbFeedback * (bufEarlyTap0[bufTapIndex] + delay0 + in0); - bufEarlyTap1[bufTapIndex] = reverbFeedback * (bufEarlyTap1[bufTapIndex] + delay1 + in1); - - bufTapIndex = (bufTapIndex + 1) % bufSizeLimit; - - outputs[2].mOutput = in0 * dry + bufEarlyTap0[bufTapIndex] * wet; - outputs[3].mOutput = in1 * dry + bufEarlyTap1[bufTapIndex] * wet; - - bufRev0[bufIndex] += delay0; - bufRev1[bufIndex] += delay1; - - // buffer blur of oldest sample plus next oldest sample with global attenuation and new input - auto [reverb0, reverb1] = sample(bufRev0.data(), bufRev1.data(), earliestDelay, longestDelay, numDelays, reverbFeedback, stereoWidth); - - cutoff *= cutoff; - float rc = 1.0f - res * cutoff; - - mLP0 = rc * mLP0 - cutoff * (mLP1 + reverb0); - mLP1 = rc * mLP1 + cutoff * mLP0; - mLP2 = rc * mLP2 - cutoff * (mLP3 + reverb1); - mLP3 = rc * mLP3 + cutoff * mLP2; - - bufRev0[bufIndex] = reverbFeedback * (bufRev0[bufIndex] + mLP1); - bufRev1[bufIndex] = reverbFeedback * (bufRev1[bufIndex] + mLP3); - - bufIndex = (bufIndex + 1) % bufSizeLimit; - - outputs[0].mOutput = in0 * dry + bufRev0[bufIndex] * wet; - outputs[1].mOutput = in1 * dry + bufRev1[bufIndex] * wet; - - // cross delay mixing (delay 1 & 2) - //buf0[bufIndex] += fb1 * buf1[delay1]; - //buf1[bufIndex] += fb0 * buf0[delay0]; - - // central delay mixing (delay 3) - //float centralDelay = fb2 * (buf0[delay2] + buf1[delay2]); - //buf0[bufIndex] += centralDelay; - //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(int32_t, 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 = inVal0 > inVal1 ? 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(int32_t, 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 = in0 > in1 ? 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(int32_t, 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(); - - float inPreamp0 = in0 * preamp; - float inPreamp1 = in1 * preamp; - - if (inPreamp0 >= limit || inPreamp0 <= -limit) { - if (inPreamp0 > 0.0f) { - inPreamp0 = limit + (1.0f - limit) * l::math::functions::sigmoidFast((inPreamp0 - limit) / ((1.0f - limit) * 1.5f)); - } - else { - inPreamp0 = -(limit + (1.0f - limit) * l::math::functions::sigmoidFast((-inPreamp0 - limit) / ((1.0f - limit) * 1.5f))); - } - } - if (inPreamp1 >= limit || inPreamp1 <= -limit) { - if (inPreamp1 > 0.0f) { - inPreamp1 = limit + (1.0f - limit) * l::math::functions::sigmoidFast((inPreamp1 - limit) / ((1.0f - limit) * 1.5f)); - } - else { - inPreamp1 = -(limit + (1.0f - limit) * l::math::functions::sigmoidFast((-inPreamp1 - limit) / ((1.0f - limit) * 1.5f))); - } - } - - outputs.at(0).mOutput = dry * in0 + wet * inPreamp0; - 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(); - float fmod = inputs.at(3).Get(); - 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 fmodPerPattern = fmod / static_cast(patternSize); - - mGateSmoothing = attack * attack; - mGateSmoothingNeg = mGateSmoothing * 0.25f; - - float freq = 44100.0f * 60.0f / bpm; - - mSamplesUntilUpdate = l::audio::BatchUpdate(freq * fmodPerPattern, mSamplesUntilUpdate, 0, numSamples, - [&]() { - mGateIndex %= gate.size(); - mGainTarget = gate[mGateIndex]; - mGateIndex++; - }, - [&](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++) { - outputs.at(i).mOutput = inputs.at(i).Get(); - } - } - - void GraphInputKeyboardPiano::Tick(int32_t, float) { - mKeyboard.Update(); - } - - void GraphInputKeyboardPiano::Reset() { - for (int8_t i = 0; i < GetNumInputs(); i++) { - mNode->SetInput(i, 0.0f); - } - } - - void GraphInputKeyboardPiano::NoteOn(int32_t note) { - float frequency = l::audio::GetFrequencyFromNote(static_cast(note)); - int8_t channel = GetNextNoteChannel(note); - mNode->SetInput(static_cast(channel), frequency); - } - void GraphInputKeyboardPiano::NoteOff() { - Reset(); - } - - void GraphInputKeyboardPiano::NoteOff(int32_t note) { - int8_t channel = ResetNoteChannel(note); - if (channel >= 0) { - mNode->SetInput(channel, 0.0f); - } - } - - int8_t GraphInputKeyboardPiano::ResetNoteChannel(int32_t note) { - for (size_t i = 0; i < mChannel.size(); i++) { - if (mChannel.at(i).first == note) { - mChannel.at(i).second = 0; - return static_cast(i); - } - } - // It is possible to get a note off for a note not playing because the channel was taken for another newer note - return -1; - } - - int8_t GraphInputKeyboardPiano::GetNextNoteChannel(int32_t note) { - for (size_t i = 0; i < mChannel.size(); i++) { - if (mChannel.at(i).first == note) { - mChannel.at(i).second = mNoteCounter++; - return static_cast(i); - } - } - - for (size_t i = 0; i < mChannel.size(); i++) { - if (mChannel.at(i).first == 0) { - mChannel.at(i).first = note; - mChannel.at(i).second = mNoteCounter++; - return static_cast(i); - } - } - - int32_t lowestCount = INT32_MAX; - int8_t lowestCountIndex = 0; - for (size_t i = 0; i < mChannel.size(); i++) { - if (lowestCount > mChannel.at(i).second) { - lowestCount = mChannel.at(i).second; - lowestCountIndex = static_cast(i); - } - } - mChannel.at(lowestCountIndex).first = note; - mChannel.at(lowestCountIndex).second = mNoteCounter++; - return lowestCountIndex; - } - - /*********************************************************************/ - void GraphInputMidiKeyboard::Process(int32_t, std::vector& inputs, std::vector& outputs) { - for (size_t i = 0; i < inputs.size(); i++) { - outputs.at(i).mOutput = inputs.at(i).Get(); - } - } - - void GraphInputMidiKeyboard::Reset() { - for (int8_t i = 0; i < GetNumInputs(); i++) { - mNode->SetInput(i, 0.0f); - } - } - - void GraphInputMidiKeyboard::MidiEvent(const l::hid::midi::MidiData& data) { - //LOG(LogInfo) << "listener 1: dev" << data.device << " stat " << data.status << " ch " << data.channel << " d1 " << data.data1 << " d2 " << data.data2; - if (data.channel != 1) { - return; - } - if (data.status == 9) { - // note on - NoteOn(data.data1, data.data2); - } - else if (data.status == 8) { - // note off - NoteOff(data.data1); - } - } - - void GraphInputMidiKeyboard::NoteOn(int32_t note, int32_t velocity) { - float frequency = l::audio::GetFrequencyFromNote(static_cast(note)); - int8_t channel = GetNextNoteChannel(note); - mNode->SetInput(static_cast(channel), frequency); - mNode->SetInput(static_cast(1), velocity / 128.0f); - } - void GraphInputMidiKeyboard::NoteOff() { - Reset(); - } - - void GraphInputMidiKeyboard::NoteOff(int32_t note) { - int8_t channel = ResetNoteChannel(note); - if (channel >= 0) { - mNode->SetInput(channel, 0.0f); - } - } - - int8_t GraphInputMidiKeyboard::ResetNoteChannel(int32_t note) { - for (size_t i = 0; i < mChannel.size(); i++) { - if (mChannel.at(i).first == note) { - mChannel.at(i).second = 0; - return static_cast(i); - } - } - // It is possible to get a note off for a note not playing because the channel was taken for another newer note - return -1; - } - - int8_t GraphInputMidiKeyboard::GetNextNoteChannel(int32_t note) { - for (size_t i = 0; i < mChannel.size(); i++) { - if (mChannel.at(i).first == note) { - mChannel.at(i).second = mNoteCounter++; - return static_cast(i); - } - } - - for (size_t i = 0; i < mChannel.size(); i++) { - if (mChannel.at(i).first == 0) { - mChannel.at(i).first = note; - mChannel.at(i).second = mNoteCounter++; - return static_cast(i); - } - } - - int32_t lowestCount = INT32_MAX; - int8_t lowestCountIndex = 0; - for (size_t i = 0; i < mChannel.size(); i++) { - if (lowestCount > mChannel.at(i).second) { - lowestCount = mChannel.at(i).second; - lowestCountIndex = static_cast(i); - } - } - mChannel.at(lowestCountIndex).first = note; - mChannel.at(lowestCountIndex).second = mNoteCounter++; - return lowestCountIndex; - } - - /*********************************************************************/ - void GraphInputMidiKnobs::Process(int32_t, std::vector& inputs, std::vector& outputs) { - for (size_t i = 0; i < inputs.size(); i++) { - outputs.at(i).mOutput = inputs.at(i).Get(); - } - } - - void GraphInputMidiKnobs::Reset() { - for (int8_t i = 0; i < GetNumInputs(); i++) { - mNode->SetInput(i, 0.0f); - } - } - - void GraphInputMidiKnobs::MidiEvent(const l::hid::midi::MidiData& data) { - if (data.channel != 0) { - return; - } - if (data.status == 11) { - if (data.data1 >= 48 && data.data1 <= 55) { - mNode->SetInput(static_cast(data.data1 - 48), data.data2 / 128.0f); - } - } - } - - /*********************************************************************/ - void GraphInputMidiButtons::Reset() { - for (int8_t i = 0; i < GetNumInputs(); i++) { - mNode->SetInput(i, 0.0f); - } - } - - void GraphInputMidiButtons::Process(int32_t, std::vector& inputs, std::vector& outputs) { - for (size_t i = 0; i < inputs.size(); i++) { - outputs.at(i).mOutput = inputs.at(i).Get(); - } - } - - void GraphInputMidiButtons::Tick(int32_t, float) { - if (mInitCounter == 120){ - for (int8_t i = 0; i < 8; i++) { - if (mButtonStates.at(i) == BUTTON_ALLOCATED) { - // Set to off when allocated and output has been polled, i.e been connected and sampled - UpdateButton(i, BUTTON_OFF); - } - } - } - mInitCounter++; - } - - void GraphInputMidiButtons::MidiEvent(const l::hid::midi::MidiData& data) { - if (data.channel != 0) { - return; - } - if (data.data1 == 98) { - // shift button event - mMidiShiftState = data.status == 9 ? true : data.status == 8 ? false : mMidiShiftState; - } - - int8_t buttonIndex = static_cast(data.data1 - mButtonGroup * 8); - if (buttonIndex >= 0 && buttonIndex < 8) { - // pad button event - if (data.status == 9) { - if (mButtonStates.at(buttonIndex) <= BUTTON_ALLOCATED) { - // if button has not been polled before user input, set button to off first time - mButtonStates.at(buttonIndex) = BUTTON_OFF; - }else if (!mMidiShiftState && mButtonStates.at(buttonIndex) < BUTTON_ON) { - mButtonStates.at(buttonIndex)++; - }else if (mMidiShiftState && mButtonStates.at(buttonIndex) > BUTTON_OFF) { - mButtonStates.at(buttonIndex)--; - } - } - float outputValue = static_cast(mButtonStates.at(buttonIndex) - BUTTON_OFF) / 2.0f; - mNode->SetInput(buttonIndex, outputValue); - UpdateButton(buttonIndex, mButtonStates.at(buttonIndex)); - } - } -} diff --git a/packages/nodegraph/source/common/NodeGraphSchema.cpp b/packages/nodegraph/source/common/NodeGraphSchema.cpp index ec22038b..17f3bfef 100644 --- a/packages/nodegraph/source/common/NodeGraphSchema.cpp +++ b/packages/nodegraph/source/common/NodeGraphSchema.cpp @@ -118,8 +118,11 @@ namespace l::nodegraph { case 256: node = mMainNodeGraph.NewNode(OutputType::Default); break; + case 257: + node = mMainNodeGraph.NewNode(OutputType::Default); + break; case 300: - node = mMainNodeGraph.NewNode(OutputType::Default, 1, mKeyState); + node = mMainNodeGraph.NewNode(OutputType::Default, mKeyState); break; case 301: node = mMainNodeGraph.NewNode(OutputType::Default, mMidiManager); diff --git a/packages/nodegraph/tests/common/NodeGraphSchemaTest.cpp b/packages/nodegraph/tests/common/NodeGraphSchemaTest.cpp index 9272db0e..b9181216 100644 --- a/packages/nodegraph/tests/common/NodeGraphSchemaTest.cpp +++ b/packages/nodegraph/tests/common/NodeGraphSchemaTest.cpp @@ -2,7 +2,6 @@ #include "logging/Log.h" #include "nodegraph/NodeGraph.h" -#include "nodegraph/NodeGraphOperations.h" #include "nodegraph/NodeGraphSchema.h" using namespace l; diff --git a/packages/rendering/include/rendering/ui/UINodeEditor.h b/packages/rendering/include/rendering/ui/UINodeEditor.h index b5c380be..399def08 100644 --- a/packages/rendering/include/rendering/ui/UINodeEditor.h +++ b/packages/rendering/include/rendering/ui/UINodeEditor.h @@ -8,7 +8,6 @@ #include "rendering/ui/UICreator.h" #include "nodegraph/NodeGraph.h" -#include "nodegraph/NodeGraphOperations.h" #include "nodegraph/NodeGraphSchema.h" #include From 46cfc61b10965d09f8ef520d13db4231328b5608 Mon Sep 17 00:00:00 2001 From: lnd3 Date: Mon, 9 Sep 2024 04:58:38 +0200 Subject: [PATCH 08/42] Fix arpeggio by adding input buffers. This will also enable buffering if necessary for batch processing. Fix a few math issues. --- packages/audio/include/audio/AudioUtils.h | 3 +- packages/audio/source/common/AudioUtils.cpp | 3 +- packages/math/include/math/MathFunc.h | 2 +- packages/math/include/math/MathSmooth.h | 14 ++-- .../nodegraph/include/nodegraph/NodeGraph.h | 18 +++-- .../include/nodegraph/NodeGraphOpEffect.h | 13 ++-- .../nodegraph/source/common/NodeGraph.cpp | 64 +++++++++------ .../source/common/NodeGraphOpEffect.cpp | 78 +++++++++++-------- .../source/common/NodeGraphOpInput.cpp | 43 ++++++++-- .../rendering/source/common/ui/UIVisitors.cpp | 39 ++++++---- 10 files changed, 174 insertions(+), 103 deletions(-) diff --git a/packages/audio/include/audio/AudioUtils.h b/packages/audio/include/audio/AudioUtils.h index 65199753..2a979bcb 100644 --- a/packages/audio/include/audio/AudioUtils.h +++ b/packages/audio/include/audio/AudioUtils.h @@ -3,7 +3,8 @@ #include namespace l::audio { - extern const float gNoNote; + extern const float gNoNote_f; + extern const int32_t gNoNote; float GetFrequencyFromNote(float note); double GetPhaseModifier(double note, double modifier); float GetRCAFactor(float numSamples, float limit = 0.01f); diff --git a/packages/audio/source/common/AudioUtils.cpp b/packages/audio/source/common/AudioUtils.cpp index c42e0249..13fa3701 100644 --- a/packages/audio/source/common/AudioUtils.cpp +++ b/packages/audio/source/common/AudioUtils.cpp @@ -8,7 +8,8 @@ #include namespace l::audio { - const float gNoNote = -500.0f; + const float gNoNote_f = -500.0f; + const int32_t gNoNote = -500; float GetFrequencyFromNote(float note) { return 440.0f * l::math::functions::pow(2.0f, (note - 49.0f) / 12.0f); diff --git a/packages/math/include/math/MathFunc.h b/packages/math/include/math/MathFunc.h index 0b915b0c..5b74ad42 100644 --- a/packages/math/include/math/MathFunc.h +++ b/packages/math/include/math/MathFunc.h @@ -12,7 +12,7 @@ namespace l::math::functions { template - bool equal(T a, T b, T accuracy) { + bool equal(T a, T b, T accuracy = static_cast(0.0001)) { return abs(a - b) < accuracy; } diff --git a/packages/math/include/math/MathSmooth.h b/packages/math/include/math/MathSmooth.h index ae89130d..2186f5a8 100644 --- a/packages/math/include/math/MathSmooth.h +++ b/packages/math/include/math/MathSmooth.h @@ -12,41 +12,41 @@ namespace l::math::smooth { // Quintic Polynomial - C2 continuity template V smoothPolyC2(V x) { - return x * x * x * (x * (x * 6.0 - 15.0) + 10.0); + return x * x * x * (x * (x * static_cast(6.0) - static_cast(15.0)) + static_cast(10.0)); } template V smoothPolyC2Grad(V x) { - return 30.0 * x * x * (x * (x - 2.0) + 1.0); + return static_cast(30.0) * x * x * (x * (x - static_cast(2.0)) + static_cast(1.0)); } template V smoothPoly4(V x) { V x2 = x * x; - return 4.0 * x2 * x - 3.0 * x2 * x2; + return static_cast(4.0) * x2 * x - static_cast(3.0) * x2 * x2; } template V smoothPoly4grad(V x) { V x2 = x * x; - return 12.0 * x2 - 12.0 * x2 * x; + return static_cast(12.0) * x2 - static_cast(12.0) * x2 * x; } template V smootPolyh3(V x) { V x2 = x * x; - return 3.0 * x2 - 2.0 * x2 * x; + return static_cast(3.0) * x2 - static_cast(2.0) * x2 * x; } template V smoothPoly3grad(V x) { - return 6.0 * x - 6.0 * x * x; + return static_cast(6.0) * x - static_cast(6.0) * x * x; } template V smoothRationalC2(V x) { - return x * x * x / (3.0 * x * x - 3.0 * x + 1.0); + return x * x * x / (static_cast(3.0) * x * x - static_cast(3.0) * x + static_cast(1.0)); } } diff --git a/packages/nodegraph/include/nodegraph/NodeGraph.h b/packages/nodegraph/include/nodegraph/NodeGraph.h index a50a9ba1..74890b22 100644 --- a/packages/nodegraph/include/nodegraph/NodeGraph.h +++ b/packages/nodegraph/include/nodegraph/NodeGraph.h @@ -26,7 +26,7 @@ namespace l::nodegraph { INPUT_NODE, INPUT_CONSTANT, INPUT_VALUE, - //INPUT_ARRAY // TODO: is it possible to process batches for example for audio processing? + INPUT_ARRAY }; enum class InputBound { @@ -55,9 +55,9 @@ namespace l::nodegraph { std::unique_ptr mName = nullptr; bool mOutputPolled = false; - float& GetOutput(int32_t numSamples = 1) { + float& GetOutput(int32_t size = 1) { if (!mOutputBuf) { - if (numSamples <= 1) { + if (size <= 1) { mOutputPolled = true; return mOutput; } @@ -65,8 +65,8 @@ namespace l::nodegraph { mOutputBuf = std::make_unique>(); } } - if (static_cast(mOutputBuf->size()) < numSamples) { - mOutputBuf->resize(numSamples); + if (static_cast(mOutputBuf->size()) < size) { + mOutputBuf->resize(size); } mOutputPolled = true; return *mOutputBuf->data(); @@ -111,9 +111,10 @@ namespace l::nodegraph { int8_t mInputFromOutputChannel = 0; std::unique_ptr mName; + std::unique_ptr> mInputBuf = nullptr; void Reset(); - bool HasInput(); + bool HasInputNode(); float Get(); float& Get(int32_t numSamples); }; @@ -144,8 +145,9 @@ namespace l::nodegraph { virtual int8_t GetNumOutputs(); virtual int8_t GetNumConstants(); - virtual float& GetOutput(int8_t outputChannel, int32_t numSamples = 1); + virtual float& GetOutput(int8_t outputChannel, int32_t size = 1); virtual float GetInput(int8_t inputChannel); + virtual float& GetInput(int8_t inputChannel, int32_t size); virtual int32_t GetOutputSize(int8_t outputChannel); @@ -159,7 +161,7 @@ namespace l::nodegraph { virtual bool SetInput(int8_t inputChannel, NodeGraphBase& source, int8_t sourceOutputChannel); virtual bool SetInput(int8_t inputChannel, NodeGraphGroup& source, int8_t sourceOutputChannel); - virtual bool SetInput(int8_t inputChannel, float constant); + virtual bool SetInput(int8_t inputChannel, float constant, int32_t size = -1); virtual bool SetInput(int8_t inputChannel, float* floatPtr); virtual bool SetInputBound(int8_t inputChannel, InputBound bound = InputBound::INPUT_DONTCHANGE, float boundMin = 0.0f, float boundMax = 0.0f); diff --git a/packages/nodegraph/include/nodegraph/NodeGraphOpEffect.h b/packages/nodegraph/include/nodegraph/NodeGraphOpEffect.h index 43d77675..3cd6f90a 100644 --- a/packages/nodegraph/include/nodegraph/NodeGraphOpEffect.h +++ b/packages/nodegraph/include/nodegraph/NodeGraphOpEffect.h @@ -244,7 +244,7 @@ namespace l::nodegraph { /*********************************************************************/ class GraphEffectTranceGate : public NodeGraphOp { public: - std::string defaultInStrings[6] = { "In 1", "In 2", "Bpm", "Fmod", "Attack", "Pattern" }; + std::string defaultInStrings[7] = { "In 1", "In 2", "Bpm", "Fmod", "Attack", "Pattern", "Drift Sync"}; std::string defaultOutStrings[3] = { "Out 1", "Out 2" }; const std::vector< std::vector> patterns = { @@ -261,7 +261,7 @@ namespace l::nodegraph { }; GraphEffectTranceGate(NodeGraphBase* node) : - NodeGraphOp(node, 6, 2, 0) + NodeGraphOp(node, 7, 2, 0) {} virtual ~GraphEffectTranceGate() = default; @@ -295,11 +295,11 @@ namespace l::nodegraph { /*********************************************************************/ class GraphEffectArpeggio: public NodeGraphOp { public: - std::string defaultInStrings[6] = { "Note On Id", "Note Off Id", "Velocity", "Bpm", "Fmod", "Attack"}; + std::string defaultInStrings[7] = { "Note On Id", "Note Off Id", "Velocity", "Bpm", "Fmod", "Attack", "Drift Sync"}; std::string defaultOutStrings[3] = { "Freq", "Volume" }; GraphEffectArpeggio(NodeGraphBase* node) : - NodeGraphOp(node, 6, 2, 0) + NodeGraphOp(node, 7, 2, 0) {} virtual ~GraphEffectArpeggio() = default; @@ -328,11 +328,8 @@ namespace l::nodegraph { float mGainSmoothing = 0.01f; float mGainSmoothingNeg = 0.01f; - float mNoteOnId = l::audio::gNoNote; - float mNoteOffId = l::audio::gNoNote; - float mCurrentNoteFreq = 0.0f; - std::vector mNotes; + std::vector mNotes; int32_t mNoteIndex = 0; }; } diff --git a/packages/nodegraph/source/common/NodeGraph.cpp b/packages/nodegraph/source/common/NodeGraph.cpp index 9886f917..db9f47f6 100644 --- a/packages/nodegraph/source/common/NodeGraph.cpp +++ b/packages/nodegraph/source/common/NodeGraph.cpp @@ -91,8 +91,8 @@ namespace l::nodegraph { mLastTickCount = tickCount; } - float& NodeGraphBase::GetOutput(int8_t outputChannel, int32_t numSamples) { - return mOutputs.at(outputChannel).GetOutput(numSamples); + float& NodeGraphBase::GetOutput(int8_t outputChannel, int32_t size) { + return mOutputs.at(outputChannel).GetOutput(size); } int32_t NodeGraphBase::GetOutputSize(int8_t outputChannel) { @@ -103,13 +103,17 @@ namespace l::nodegraph { return mInputs.at(inputChannel).Get(); } + float& NodeGraphBase::GetInput(int8_t inputChannel, int32_t size) { + return mInputs.at(inputChannel).Get(size); + } + bool NodeGraphBase::ClearInput(int8_t inputChannel) { ASSERT(inputChannel >= 0 && static_cast(inputChannel) < mInputs.size()); if (!IsValidInOutNum(inputChannel, mInputs.size())) { return false; } auto& input = mInputs.at(inputChannel); - if (!input.HasInput()) { + if (!input.HasInputNode()) { return false; } input.mInputType = InputType::INPUT_EMPTY; @@ -126,7 +130,7 @@ namespace l::nodegraph { auto& input = mInputs.at(inputChannel); if (!IsValidInOutNum(sourceOutputChannel, source.mOutputs.size()) || !IsValidInOutNum(inputChannel, mInputs.size()) || - input.HasInput()) { + input.HasInputNode()) { return false; } input.mInput.mInputNode = &source; @@ -144,7 +148,7 @@ namespace l::nodegraph { if (source.ContainsNode(GetId())) { if (!IsValidInOutNum(sourceChannel, source.GetInputNode().GetNumOutputs()) || !IsValidInOutNum(inputChannel, mInputs.size()) || - input.HasInput()) { + input.HasInputNode()) { return false; } input.mInput.mInputNode = &source.GetInputNode(); @@ -153,7 +157,7 @@ namespace l::nodegraph { else { if (!IsValidInOutNum(sourceChannel, source.GetOutputNode().GetNumOutputs()) || !IsValidInOutNum(inputChannel, mInputs.size()) || - input.HasInput()) { + input.HasInputNode()) { return false; } input.mInput.mInputNode = &source.GetOutputNode(); @@ -163,15 +167,25 @@ namespace l::nodegraph { return true; } - bool NodeGraphBase::SetInput(int8_t inputChannel, float constant) { + bool NodeGraphBase::SetInput(int8_t inputChannel, float constant, int32_t size) { ASSERT(inputChannel >= 0 && static_cast(inputChannel) < mInputs.size()); if (!IsValidInOutNum(inputChannel, mInputs.size())) { return false; } auto& input = mInputs.at(inputChannel); - input.mInput.mInputFloatConstant = constant; - input.mInputType = InputType::INPUT_CONSTANT; - input.mInputFromOutputChannel = 0; + if (size <= 0) { + input.mInput.mInputFloatConstant = constant; + input.mInputType = InputType::INPUT_CONSTANT; + input.mInputFromOutputChannel = 0; + } + else { + input.mInputType = InputType::INPUT_ARRAY; + input.mInputFromOutputChannel = 0; + auto inputBuf = &input.Get(size); + for (int32_t i = 0; i < size; i++) { + *inputBuf++ = constant; + } + } return true; } @@ -346,19 +360,9 @@ namespace l::nodegraph { } } - bool NodeGraphInput::HasInput() { - switch (mInputType) { - case InputType::INPUT_NODE: - if (mInput.mInputNode != nullptr) { - return true; - } - break; - case InputType::INPUT_CONSTANT: - break; - case InputType::INPUT_VALUE: - return mInput.mInputFloat != nullptr; - case InputType::INPUT_EMPTY: - break; + bool NodeGraphInput::HasInputNode() { + if (mInputType == InputType::INPUT_NODE) { + return true; } return false; } @@ -374,6 +378,8 @@ namespace l::nodegraph { case InputType::INPUT_CONSTANT: value = mInput.mInputFloatConstant; break; + case InputType::INPUT_ARRAY: + return *mInputBuf->data(); case InputType::INPUT_VALUE: value = *mInput.mInputFloat; break; @@ -383,13 +389,21 @@ namespace l::nodegraph { return l::math::functions::clamp(value, mBoundMin, mBoundMax); } - float& NodeGraphInput::Get(int32_t numSamples) { + float& NodeGraphInput::Get(int32_t size) { switch (mInputType) { case InputType::INPUT_NODE: if (mInput.mInputNode != nullptr) { - return mInput.mInputNode->GetOutput(mInputFromOutputChannel, numSamples); + return mInput.mInputNode->GetOutput(mInputFromOutputChannel, size); } break; + case InputType::INPUT_ARRAY: + if (!mInputBuf) { + mInputBuf = std::make_unique>(); + } + if (static_cast(mInputBuf->size()) < size) { + mInputBuf->resize(size); + } + return *mInputBuf->data(); case InputType::INPUT_CONSTANT: return mInput.mInputFloatConstant; case InputType::INPUT_VALUE: diff --git a/packages/nodegraph/source/common/NodeGraphOpEffect.cpp b/packages/nodegraph/source/common/NodeGraphOpEffect.cpp index ca79be2e..c5b04442 100644 --- a/packages/nodegraph/source/common/NodeGraphOpEffect.cpp +++ b/packages/nodegraph/source/common/NodeGraphOpEffect.cpp @@ -4,6 +4,7 @@ #include "audio/AudioUtils.h" #include "math/MathFunc.h" +#include "math/MathSmooth.h" #include @@ -351,10 +352,12 @@ namespace l::nodegraph { mNode->SetInput(3, 1.0f); mNode->SetInput(4, 0.001f); mNode->SetInput(5, 0.0f); + mNode->SetInput(6, 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); + mNode->SetInputBound(6, InputBound::INPUT_0_TO_1); } void GraphEffectTranceGate::Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) { @@ -364,6 +367,10 @@ namespace l::nodegraph { float fmod = inputs.at(3).Get(); float attack = inputs.at(4).Get(); + if (inputs.at(6).Get() > 0.5f) { + mSamplesUntilUpdate = 0.0f; + } + size_t patternsSize = patterns.size(); int32_t patternId = static_cast(patternsSize * inputs.at(5).Get()); auto& gate = patterns[patternId % patternsSize]; @@ -372,7 +379,7 @@ namespace l::nodegraph { float fmodPerPattern = fmod / static_cast(patternSize); mGateSmoothing = attack * attack; - mGateSmoothingNeg = mGateSmoothing * 0.25f; + mGateSmoothingNeg = mGateSmoothing; float freq = 44100.0f * 60.0f / bpm; @@ -389,10 +396,10 @@ namespace l::nodegraph { float delta = mGainTarget - mGain; if (delta > 0) { - mGain += mGateSmoothing * delta; + mGain += mGateSmoothing * l::math::smooth::smootPolyh3(delta); } else { - mGain += mGateSmoothingNeg * delta; + mGain += mGateSmoothingNeg * (-l::math::smooth::smootPolyh3(-delta)); } outputs.at(0).mOutput = mGain * in0; @@ -407,59 +414,68 @@ namespace l::nodegraph { mGainTarget = 0.0f; - mNode->SetInput(0, l::audio::gNoNote); - mNode->SetInput(1, l::audio::gNoNote); + mNode->SetInput(0, l::audio::gNoNote_f, 8); + mNode->SetInput(1, l::audio::gNoNote_f, 8); + mNode->SetInput(2, 1.0f); mNode->SetInput(3, 60.0f); mNode->SetInput(4, 1.0f); mNode->SetInput(5, 0.01f); + mNode->SetInput(6, 0.0f); mNode->SetInputBound(2, InputBound::INPUT_0_TO_1); mNode->SetInputBound(3, InputBound::INPUT_CUSTOM, 1.0f, 1000.0f); mNode->SetInputBound(4, InputBound::INPUT_CUSTOM, 0.01f, 1.0f); mNode->SetInputBound(5, InputBound::INPUT_0_TO_1); + mNode->SetInputBound(6, InputBound::INPUT_0_TO_1); } void GraphEffectArpeggio::Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) { // "Note On Id", "Note Off Id", "Velocity", "Bpm", "Fmod", "Attack" - float noteOnId = inputs.at(0).Get(); - float noteOffId = inputs.at(1).Get(); + auto noteIdsOn = &inputs.at(0).Get(8); + auto noteIdsOff = &inputs.at(1).Get(8); float velocity = inputs.at(2).Get(); float bpm = inputs.at(3).Get(); float fmod = inputs.at(4).Get(); float attack = inputs.at(5).Get(); - if (noteOffId > l::audio::gNoNote) { - if (!l::math::functions::equal(mNoteOffId, noteOffId, 0.1f) && !mNotes.empty()) { - auto it = std::find_if(mNotes.begin(), mNotes.end(), [&](const float& note) { - return l::math::functions::equal(note, noteOffId, 0.1f); - }); + if (inputs.at(6).Get() > 0.5f) { + mSamplesUntilUpdate = 0.0f; + } + + if (!mNotes.empty()) { + for (int32_t i = 0; i < 8; i++) { + if (l::math::functions::equal(*noteIdsOff, l::audio::gNoNote_f)) { + break; + } + int32_t noteOffId = static_cast(*noteIdsOff + 0.5f); + auto it = std::find(mNotes.begin(), mNotes.end(), noteOffId); if (it != mNotes.end()) { mNotes.erase(it); - mNoteOffId = noteOffId; - mNoteOnId = -l::math::constants::FLTMAX; } + noteIdsOff++; } } - if (noteOnId > l::audio::gNoNote) { - if (!l::math::functions::equal(mNoteOnId, noteOnId, 0.1f)) { - if (mNotes.empty()) { - mSamplesUntilUpdate = 0.0f; - } - auto it = std::find_if(mNotes.begin(), mNotes.end(), [&](const float& note) { - return l::math::functions::equal(note, noteOnId, 0.1f); - }); - if (it == mNotes.end()) { - mNotes.push_back(noteOnId); - mNoteOnId = noteOnId; - mNoteOffId = -l::math::constants::FLTMAX; - } + + if (mNotes.empty()) { + mSamplesUntilUpdate = 0.0f; + mGainTarget = 0.0f; + } + for (int32_t i = 0; i < 8; i++) { + if (l::math::functions::equal(*noteIdsOn, l::audio::gNoNote_f)) { + break; + } + int32_t noteOnId = static_cast(*noteIdsOn + 0.5f); + auto it = std::find(mNotes.begin(), mNotes.end(), noteOnId); + if (it == mNotes.end()) { + mNotes.push_back(noteOnId); } + noteIdsOn++; } mGainSmoothing = attack * attack; - mGainSmoothingNeg = mGainSmoothing * 0.25f; + mGainSmoothingNeg = mGainSmoothing; float freq = 44100.0f * 60.0f / bpm; @@ -470,7 +486,7 @@ namespace l::nodegraph { } else { mNoteIndex = mNoteIndex % mNotes.size(); - mCurrentNoteFreq = l::audio::GetFrequencyFromNote(mNotes.at(mNoteIndex)); + mCurrentNoteFreq = l::audio::GetFrequencyFromNote(static_cast(mNotes.at(mNoteIndex))); mNoteIndex++; mGainTarget = velocity; } @@ -479,10 +495,10 @@ namespace l::nodegraph { for (int32_t i = start; i < end; i++) { float delta = mGainTarget - mGain; if (delta > 0) { - mGain += mGainSmoothing * delta; + mGain += mGainSmoothing * l::math::smooth::smootPolyh3(delta); } else { - mGain += mGainSmoothingNeg * delta; + mGain += mGainSmoothingNeg * (-l::math::smooth::smootPolyh3(-delta)); } outputs.at(0).mOutput = mCurrentNoteFreq; diff --git a/packages/nodegraph/source/common/NodeGraphOpInput.cpp b/packages/nodegraph/source/common/NodeGraphOpInput.cpp index c0f97822..852c9eba 100644 --- a/packages/nodegraph/source/common/NodeGraphOpInput.cpp +++ b/packages/nodegraph/source/common/NodeGraphOpInput.cpp @@ -14,6 +14,8 @@ namespace l::nodegraph { for (size_t i = 0; i < inputs.size(); i++) { outputs.at(i).mOutput = inputs.at(i).Get(); } + mNode->SetInput(1, static_cast(l::audio::gNoNote_f)); + mNode->SetInput(2, static_cast(l::audio::gNoNote_f)); } void GraphInputKeyboardPiano::Tick(int32_t, float) { @@ -22,8 +24,8 @@ namespace l::nodegraph { void GraphInputKeyboardPiano::Reset() { mNode->SetInput(0, 0.0f); - mNode->SetInput(1, l::audio::gNoNote); - mNode->SetInput(2, l::audio::gNoNote); + mNode->SetInput(1, l::audio::gNoNote_f); + mNode->SetInput(2, l::audio::gNoNote_f); } void GraphInputKeyboardPiano::NoteOn(int32_t note) { @@ -81,16 +83,26 @@ namespace l::nodegraph { /*********************************************************************/ void GraphInputMidiKeyboard::Process(int32_t, std::vector& inputs, std::vector& outputs) { - for (size_t i = 0; i < inputs.size(); i++) { - outputs.at(i).mOutput = inputs.at(i).Get(); + outputs.at(0).mOutput = inputs.at(0).Get(); + outputs.at(1).mOutput = inputs.at(1).Get(); + + auto output2 = &outputs.at(2).GetOutput(8); + auto output3 = &outputs.at(3).GetOutput(8); + auto input2 = &inputs.at(2).Get(8); + auto input3 = &inputs.at(3).Get(8); + for (int32_t i = 0; i < 8; i++) { + *output2++ = *input2; + *output3++ = *input3; + *input2++ = l::audio::gNoNote_f; + *input3++ = l::audio::gNoNote_f; } } void GraphInputMidiKeyboard::Reset() { mNode->SetInput(0, 0.0f); mNode->SetInput(1, 0.0f); - mNode->SetInput(2, l::audio::gNoNote); - mNode->SetInput(3, l::audio::gNoNote); + mNode->SetInput(2, l::audio::gNoNote_f, 8); + mNode->SetInput(3, l::audio::gNoNote_f, 8); } void GraphInputMidiKeyboard::MidiEvent(const l::hid::midi::MidiData& data) { @@ -112,14 +124,29 @@ namespace l::nodegraph { float frequency = l::audio::GetFrequencyFromNote(static_cast(note)); mNode->SetInput(0, frequency); mNode->SetInput(1, velocity / 128.0f); - mNode->SetInput(2, static_cast(note)); + + auto input2 = &mNode->GetInput(2, 8); + for (int32_t i = 0; i < 8; i++) { + if (l::math::functions::equal(*input2, l::audio::gNoNote_f)) { + *input2 = static_cast(note); + break; + } + input2++; + } } void GraphInputMidiKeyboard::NoteOff() { Reset(); } void GraphInputMidiKeyboard::NoteOff(int32_t note) { - mNode->SetInput(3, static_cast(note)); + auto input3 = &mNode->GetInput(3, 8); + for (int32_t i = 0; i < 8; i++) { + if (l::math::functions::equal(*input3, l::audio::gNoNote_f)) { + *input3 = static_cast(note); + break; + } + input3++; + } } int8_t GraphInputMidiKeyboard::ResetNoteChannel(int32_t note) { diff --git a/packages/rendering/source/common/ui/UIVisitors.cpp b/packages/rendering/source/common/ui/UIVisitors.cpp index eb6cea3a..4e9d9546 100644 --- a/packages/rendering/source/common/ui/UIVisitors.cpp +++ b/packages/rendering/source/common/ui/UIVisitors.cpp @@ -209,25 +209,38 @@ namespace l::ui { if (mNGSchema) { auto node = mNGSchema->GetNode(container.GetNodeId()); auto nodeChannel = static_cast(container.GetChannelId()); - if (nodeChannel < node->GetNumInputs() && node->IsDataEditable(nodeChannel)) { - auto nodeValue = node->GetInput(nodeChannel); - if (!ImGui::IsKeyDown(ImGuiKey::ImGuiKey_LeftShift)) { - if (!ImGui::IsKeyDown(ImGuiKey::ImGuiKey_LeftCtrl)) { - nodeValue -= move.y / 100.0f; + if (node->IsDataEditable(nodeChannel)) { + float* nodeValue = nullptr; + if (nodeChannel < node->GetNumInputs() + node->GetNumConstants()) { + nodeValue = &node->GetInput(nodeChannel, 1); + } + else if (nodeChannel < node->GetNumOutputs()) { + nodeValue = &node->GetOutput(nodeChannel, 1); + } + if (nodeValue != nullptr) { + if (!ImGui::IsKeyDown(ImGuiKey::ImGuiKey_LeftShift)) { + if (!ImGui::IsKeyDown(ImGuiKey::ImGuiKey_LeftCtrl)) { + *nodeValue -= move.y / 100.0f; + } + else { + *nodeValue -= move.y / 10000.0f; + } } else { - nodeValue -= move.y / 10000.0f; + if (!ImGui::IsKeyDown(ImGuiKey::ImGuiKey_LeftCtrl)) { + *nodeValue -= move.y; + } + else { + *nodeValue -= 1000.0f * move.y; + } } - } - else { - if (!ImGui::IsKeyDown(ImGuiKey::ImGuiKey_LeftCtrl)) { - nodeValue -= move.y; + if (nodeChannel < node->GetNumInputs()) { + node->SetInput(nodeChannel, *nodeValue); } - else { - nodeValue -= 1000.0f * move.y; + else if (nodeChannel < node->GetNumOutputs()) { + node->GetOutput(nodeChannel, 1) = *nodeValue; } } - node->SetInput(nodeChannel, nodeValue); } } From a2612734f1906933cf50ee531948f5af89f0570f Mon Sep 17 00:00:00 2001 From: lnd3 Date: Mon, 9 Sep 2024 12:44:28 +0200 Subject: [PATCH 09/42] Improve tone generators. Add saw. Fix envelope. --- .../nodegraph/include/nodegraph/NodeGraph.h | 1 + .../include/nodegraph/NodeGraphOpEffect.h | 36 ++++++ .../include/nodegraph/NodeGraphOpFilter.h | 36 ------ .../include/nodegraph/NodeGraphOpInput.h | 2 + .../include/nodegraph/NodeGraphOpSource.h | 58 ++++++++-- .../include/nodegraph/NodeGraphSchema.h | 3 +- .../nodegraph/source/common/NodeGraph.cpp | 4 + .../source/common/NodeGraphOpEffect.cpp | 79 +++++++++++++ .../source/common/NodeGraphOpFilter.cpp | 79 ------------- .../source/common/NodeGraphOpInput.cpp | 46 ++++++-- .../source/common/NodeGraphOpSource.cpp | 106 ++++++++++++++++-- .../source/common/NodeGraphSchema.cpp | 5 +- 12 files changed, 312 insertions(+), 143 deletions(-) diff --git a/packages/nodegraph/include/nodegraph/NodeGraph.h b/packages/nodegraph/include/nodegraph/NodeGraph.h index 74890b22..ee04b7a9 100644 --- a/packages/nodegraph/include/nodegraph/NodeGraph.h +++ b/packages/nodegraph/include/nodegraph/NodeGraph.h @@ -33,6 +33,7 @@ namespace l::nodegraph { INPUT_DONTCHANGE, INPUT_UNBOUNDED, INPUT_0_TO_1, + INPUT_0_TO_2, INPUT_NEG_1_POS_1, INPUT_0_100, INPUT_CUSTOM, diff --git a/packages/nodegraph/include/nodegraph/NodeGraphOpEffect.h b/packages/nodegraph/include/nodegraph/NodeGraphOpEffect.h index 3cd6f90a..6ad33142 100644 --- a/packages/nodegraph/include/nodegraph/NodeGraphOpEffect.h +++ b/packages/nodegraph/include/nodegraph/NodeGraphOpEffect.h @@ -175,6 +175,42 @@ namespace l::nodegraph { float mEnvelope = 0.0f; }; + /*********************************************************************/ + class GraphEffectEnvelope : public NodeGraphOp { + public: + std::string defaultInStrings[5] = { "Freq", "Velocity", "Attack", "Release", "Fade" }; + std::string defaultOutStrings[2] = { "Freq", "Volume" }; + + GraphEffectEnvelope(NodeGraphBase* node) : + NodeGraphOp(node, 5, 2) + {} + + virtual ~GraphEffectEnvelope() = default; + virtual void Reset() override; + virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; + virtual void Tick(int32_t, float) {} + 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 bool IsDataVisible(int8_t channel) override { + return channel >= 1 ? true : false; + } + virtual bool IsDataEditable(int8_t channel) override { + return channel >= 1 ? true : false; + } + virtual std::string_view GetName() override { + return "Envelope"; + } + protected: + int32_t mFrameCount = 0; + float mEnvelopeTarget = 0.0f; + float mFreq = 0.0f; + float mEnvelope = 0.0f; + }; + /*********************************************************************/ class GraphEffectEnvelopeFollower : public NodeGraphOp { public: diff --git a/packages/nodegraph/include/nodegraph/NodeGraphOpFilter.h b/packages/nodegraph/include/nodegraph/NodeGraphOpFilter.h index 34f27be0..8efca744 100644 --- a/packages/nodegraph/include/nodegraph/NodeGraphOpFilter.h +++ b/packages/nodegraph/include/nodegraph/NodeGraphOpFilter.h @@ -55,41 +55,5 @@ namespace l::nodegraph { float mState1 = 0.0f; }; - /*********************************************************************/ - class GraphFilterEnvelope : public NodeGraphOp { - public: - std::string defaultInStrings[5] = { "Freq", "Velocity", "Attack", "Release", "Fade"}; - std::string defaultOutStrings[2] = { "Freq", "Volume"}; - - GraphFilterEnvelope(NodeGraphBase* node) : - NodeGraphOp(node, 5, 2) - {} - - virtual ~GraphFilterEnvelope() = default; - virtual void Reset() override; - virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; - virtual void Tick(int32_t, float) {} - 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 bool IsDataVisible(int8_t channel) override { - return channel >= 1 ? true : false; - } - virtual bool IsDataEditable(int8_t channel) override { - return channel >= 1 ? true : false; - } - virtual std::string_view GetName() override { - return "Envelope"; - } - protected: - int32_t mFrameCount = 0; - float mEnvelopeTarget = 0.0f; - float mFreq = 0.0f; - float mEnvelope = 0.0f; - }; - } diff --git a/packages/nodegraph/include/nodegraph/NodeGraphOpInput.h b/packages/nodegraph/include/nodegraph/NodeGraphOpInput.h index c438e81e..bd1e8aa9 100644 --- a/packages/nodegraph/include/nodegraph/NodeGraphOpInput.h +++ b/packages/nodegraph/include/nodegraph/NodeGraphOpInput.h @@ -55,6 +55,7 @@ namespace l::nodegraph { int8_t ResetNoteChannel(int32_t note); int8_t GetNextNoteChannel(int32_t note); + int32_t mLastNote = 0; int8_t mNoteCounter = 0; std::vector> mChannel; l::hid::KeyboardPiano mKeyboard; @@ -99,6 +100,7 @@ namespace l::nodegraph { l::hid::midi::MidiManager* mMidiManager = nullptr; + int32_t mLastNote = 0; int8_t mNoteCounter = 0; std::vector> mChannel; }; diff --git a/packages/nodegraph/include/nodegraph/NodeGraphOpSource.h b/packages/nodegraph/include/nodegraph/NodeGraphOpSource.h index e07a62f4..86571c87 100644 --- a/packages/nodegraph/include/nodegraph/NodeGraphOpSource.h +++ b/packages/nodegraph/include/nodegraph/NodeGraphOpSource.h @@ -105,21 +105,22 @@ namespace l::nodegraph { return defaultOutStrings[outputChannel]; } virtual std::string_view GetName() override { - return "Sine FM"; + return "Sine"; } protected: - double mFreq = 0.0f; + double mFreq = 0.0; float mVolume = 0.0f; - double mFmod = 0.0f; - double mPmod = 0.0f; + double mFmod = 0.0; + double mPmod = 0.0; float mReset = 0.0f; - double mWave = 0.0f; - double mDeltaTime = 0.0f; + double mWave = 0.0; + double mDeltaTime = 0.0; float mVol = 0.0f; - double mPhase = 0.0f; - double mPhaseMod = 0.0f; + double mPhase = 0.0; + double mPhaseFmod = 0.0; + float mSamplesUntilUpdate = 0.0f; }; @@ -145,7 +146,7 @@ namespace l::nodegraph { return defaultOutStrings[outputChannel]; } virtual std::string_view GetName() override { - return "Sine FM"; + return "Sine FM 1"; } protected: double mFreq = 0.0; @@ -248,5 +249,44 @@ namespace l::nodegraph { float mSamplesUntilUpdate = 0.0f; }; + /*********************************************************************/ + class GraphSourceSaw : public NodeGraphOp { + public: + GraphSourceSaw(NodeGraphBase* node) : + NodeGraphOp(node, 5, 1) + {} + + std::string defaultInStrings[5] = { "Freq", "Volume", "Fmod", "Phase", "Reset" }; + std::string defaultOutStrings[1] = { "Out" }; + + virtual ~GraphSourceSaw() = default; + virtual void Reset() override; + virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; + virtual bool IsDataVisible(int8_t) override { return true; } + virtual bool IsDataEditable(int8_t) override { return true; } + 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 "Saw"; + } + protected: + double mFreq = 0.0; + float mVolume = 0.0f; + double mFmod = 0.0; + double mPmod = 0.0; + float mReset = 0.0f; + + double mDeltaTime = 0.0; + float mVol = 0.0f; + + double mPhase = 0.0; + double mPhaseFmod = 0.0; + double mWave = 0.0; + float mSamplesUntilUpdate = 0.0f; + }; } diff --git a/packages/nodegraph/include/nodegraph/NodeGraphSchema.h b/packages/nodegraph/include/nodegraph/NodeGraphSchema.h index 3536d8b2..62e70205 100644 --- a/packages/nodegraph/include/nodegraph/NodeGraphSchema.h +++ b/packages/nodegraph/include/nodegraph/NodeGraphSchema.h @@ -46,9 +46,10 @@ namespace l::nodegraph { RegisterNodeType("Source", 3, "Value [-inf,inf]"); RegisterNodeType("Source", 4, "Time"); RegisterNodeType("Source", 5, "Sine"); - RegisterNodeType("Source", 6, "Sine FM"); + RegisterNodeType("Source", 6, "Sine FM 1"); RegisterNodeType("Source", 7, "Sine FM 2"); RegisterNodeType("Source", 8, "Sine FM 3"); + RegisterNodeType("Source", 9, "Saw"); RegisterNodeType("Numeric", 50, "Add"); RegisterNodeType("Numeric", 51, "Subtract"); RegisterNodeType("Numeric", 52, "Negate"); diff --git a/packages/nodegraph/source/common/NodeGraph.cpp b/packages/nodegraph/source/common/NodeGraph.cpp index db9f47f6..95a099e1 100644 --- a/packages/nodegraph/source/common/NodeGraph.cpp +++ b/packages/nodegraph/source/common/NodeGraph.cpp @@ -214,6 +214,10 @@ namespace l::nodegraph { input.mBoundMin = 0.0f; input.mBoundMax = 1.0f; break; + case InputBound::INPUT_0_TO_2: + input.mBoundMin = 0.0f; + input.mBoundMax = 2.0f; + break; case InputBound::INPUT_NEG_1_POS_1: input.mBoundMin = -1.0f; input.mBoundMax = 1.0f; diff --git a/packages/nodegraph/source/common/NodeGraphOpEffect.cpp b/packages/nodegraph/source/common/NodeGraphOpEffect.cpp index c5b04442..f35dd0b2 100644 --- a/packages/nodegraph/source/common/NodeGraphOpEffect.cpp +++ b/packages/nodegraph/source/common/NodeGraphOpEffect.cpp @@ -263,6 +263,85 @@ namespace l::nodegraph { outputs.at(2).mOutput = envelopeAbs; } + /*********************************************************************/ + void GraphEffectEnvelope::Reset() { + mEnvelope = 0.0f; + mNode->SetInput(1, 0.5f); + mNode->SetInput(2, 50.0f); + mNode->SetInput(3, 50.0f); + mNode->SetInput(4, 0.1f); + mNode->SetInputBound(1, InputBound::INPUT_0_TO_1); + mNode->SetInputBound(2, InputBound::INPUT_CUSTOM, 1.0f, 100000.0f); + mNode->SetInputBound(3, InputBound::INPUT_CUSTOM, 1.0f, 100000.0f); + mNode->SetInputBound(4, InputBound::INPUT_CUSTOM, 0.0001f, 1.0f); + } + + void GraphEffectEnvelope::Process(int32_t, std::vector& inputs, std::vector& outputs) { + float freqTarget = inputs.at(0).Get(); + float velocity = l::math::functions::pow(inputs.at(1).Get(), 0.5f); + int32_t attackFrames = static_cast(inputs.at(2).Get() * 44100.0f / 1000.0f); + int32_t releaseFrames = static_cast(inputs.at(3).Get() * 44100.0f / 1000.0f); + float freqFade = inputs.at(4).Get(); + + float attackFade = 1.0f - l::math::functions::pow(0.001f, 1.0f / (inputs.at(2).Get() * 44100.0f * 0.001f)); + float releaseFade = 1.0f - l::math::functions::pow(0.001f, 1.0f / (inputs.at(3).Get() * 44100.0f * 0.001f)); + + if (freqTarget == 0.0f) { + // trigger release + if (mFrameCount > 0 && mFrameCount < attackFrames + 1) { + mFrameCount = attackFrames + 2; + } + else if (mFrameCount > attackFrames + 1 + releaseFrames) { + mFrameCount = 0; + } + } + else { + if (mFrameCount == 0) { + mFreq = freqTarget; + } + else if (freqTarget != 0) { + mFrameCount = attackFrames + 2; + mEnvelopeTarget = velocity; + } + } + + if (freqTarget != 0 && mFrameCount < attackFrames) { + // attack + mEnvelopeTarget = velocity; + mFrameCount++; + } + else if (freqTarget != 0 && mFrameCount == attackFrames + 1) { + // sustain + } + else if (freqTarget == 0 && mFrameCount > attackFrames + 1) { + // release + mEnvelopeTarget = 0.0f; + mFrameCount++; + if (mFrameCount > attackFrames + 1 + releaseFrames) { + mFrameCount = 0; + } + } + + float delta = mEnvelopeTarget - mEnvelope; + if (delta > 0) { + mEnvelope += attackFade * delta; + } + else { + mEnvelope += releaseFade * delta; + } + + if (freqTarget != 0.0f) { + // note on + mFreq += freqFade * freqFade * (freqTarget - mFreq); + } + else { + // note off + } + + outputs.at(0).mOutput = mFreq; + outputs.at(1).mOutput = l::math::functions::pow(mEnvelope, 0.5f); + } + /*********************************************************************/ void GraphEffectEnvelopeFollower::Reset() { mEnvelope = 0.0f; diff --git a/packages/nodegraph/source/common/NodeGraphOpFilter.cpp b/packages/nodegraph/source/common/NodeGraphOpFilter.cpp index 0cd9af55..6c73d713 100644 --- a/packages/nodegraph/source/common/NodeGraphOpFilter.cpp +++ b/packages/nodegraph/source/common/NodeGraphOpFilter.cpp @@ -35,83 +35,4 @@ namespace l::nodegraph { outputs.at(0).mOutput = -mState1; } - /*********************************************************************/ - void GraphFilterEnvelope::Reset() { - mEnvelope = 0.0f; - mNode->SetInput(1, 0.5f); - mNode->SetInput(2, 50.0f); - mNode->SetInput(3, 50.0f); - mNode->SetInput(4, 0.1f); - mNode->SetInputBound(1, InputBound::INPUT_0_TO_1); - mNode->SetInputBound(2, InputBound::INPUT_CUSTOM, 1.0f, 100000.0f); - mNode->SetInputBound(3, InputBound::INPUT_CUSTOM, 1.0f, 100000.0f); - mNode->SetInputBound(4, InputBound::INPUT_CUSTOM, 0.0001f, 1.0f); - } - - void GraphFilterEnvelope::Process(int32_t, std::vector& inputs, std::vector& outputs) { - float freqTarget = inputs.at(0).Get(); - float velocity = l::math::functions::pow(inputs.at(1).Get(), 0.5f); - int32_t attackFrames = static_cast(inputs.at(2).Get() * 44100.0f / 1000.0f); - int32_t releaseFrames = static_cast(inputs.at(3).Get() * 44100.0f / 1000.0f); - float freqFade = inputs.at(4).Get(); - - float attackFade = 1.0f - l::math::functions::pow(0.001f, 1.0f / (inputs.at(2).Get() * 44100.0f * 0.001f)); - float releaseFade = 1.0f - l::math::functions::pow(0.001f, 1.0f / (inputs.at(3).Get() * 44100.0f * 0.001f)); - - if (freqTarget == 0.0f) { - // trigger release - if (mFrameCount > 0 && mFrameCount < attackFrames + 1) { - mFrameCount = attackFrames + 2; - } - else if (mFrameCount > attackFrames + 1 + releaseFrames) { - mFrameCount = 0; - } - } - else { - if (mFrameCount == 0) { - mFreq = freqTarget; - } - else { - mFrameCount = attackFrames + 2; - mEnvelopeTarget = velocity; - } - } - - if (freqTarget != 0 && mFrameCount < attackFrames) { - // attack - mEnvelopeTarget = velocity; - mFrameCount++; - } - else if (freqTarget != 0 && mFrameCount == attackFrames + 1){ - // sustain - } - else if (freqTarget == 0 && mFrameCount > attackFrames + 1) { - // release - mEnvelopeTarget = 0.0f; - mFrameCount++; - if (mFrameCount > attackFrames + 1 + releaseFrames) { - mFrameCount = 0; - } - } - - float delta = mEnvelopeTarget - mEnvelope; - if (delta > 0) { - mEnvelope += attackFade * delta; - } - else { - mEnvelope += releaseFade * delta; - } - - if (freqTarget != 0.0f) { - // note on - mFreq += freqFade * freqFade * (freqTarget - mFreq); - } - else { - // note off - } - - outputs.at(0).mOutput = mFreq; - outputs.at(1).mOutput = l::math::functions::pow(mEnvelope, 0.5f); - } - } diff --git a/packages/nodegraph/source/common/NodeGraphOpInput.cpp b/packages/nodegraph/source/common/NodeGraphOpInput.cpp index 852c9eba..11858fcd 100644 --- a/packages/nodegraph/source/common/NodeGraphOpInput.cpp +++ b/packages/nodegraph/source/common/NodeGraphOpInput.cpp @@ -11,11 +11,18 @@ namespace l::nodegraph { /*********************************************************************/ void GraphInputKeyboardPiano::Process(int32_t, std::vector& inputs, std::vector& outputs) { - for (size_t i = 0; i < inputs.size(); i++) { - outputs.at(i).mOutput = inputs.at(i).Get(); + outputs.at(0).mOutput = inputs.at(0).Get(); + + auto output1 = &outputs.at(1).GetOutput(8); + auto output2 = &outputs.at(2).GetOutput(8); + auto input1 = &inputs.at(1).Get(8); + auto input2 = &inputs.at(2).Get(8); + for (int32_t i = 0; i < 8; i++) { + *output1++ = *input1; + *output2++ = *input2; + *input1++ = l::audio::gNoNote_f; + *input2++ = l::audio::gNoNote_f; } - mNode->SetInput(1, static_cast(l::audio::gNoNote_f)); - mNode->SetInput(2, static_cast(l::audio::gNoNote_f)); } void GraphInputKeyboardPiano::Tick(int32_t, float) { @@ -24,21 +31,40 @@ namespace l::nodegraph { void GraphInputKeyboardPiano::Reset() { mNode->SetInput(0, 0.0f); - mNode->SetInput(1, l::audio::gNoNote_f); - mNode->SetInput(2, l::audio::gNoNote_f); + mNode->SetInput(1, l::audio::gNoNote_f, 8); + mNode->SetInput(2, l::audio::gNoNote_f, 8); } void GraphInputKeyboardPiano::NoteOn(int32_t note) { + mLastNote = note; float frequency = l::audio::GetFrequencyFromNote(static_cast(note)); mNode->SetInput(0, frequency); - mNode->SetInput(1, static_cast(note)); + + auto input1 = &mNode->GetInput(1, 8); + for (int32_t i = 0; i < 8; i++) { + if (l::math::functions::equal(*input1, l::audio::gNoNote_f)) { + *input1 = static_cast(note); + break; + } + input1++; + } } void GraphInputKeyboardPiano::NoteOff() { Reset(); } void GraphInputKeyboardPiano::NoteOff(int32_t note) { - mNode->SetInput(2, static_cast(note)); + if (mLastNote == note) { + mNode->SetInput(0, 0.0f); + } + auto input2 = &mNode->GetInput(2, 8); + for (int32_t i = 0; i < 8; i++) { + if (l::math::functions::equal(*input2, l::audio::gNoNote_f)) { + *input2 = static_cast(note); + break; + } + input2++; + } } int8_t GraphInputKeyboardPiano::ResetNoteChannel(int32_t note) { @@ -121,6 +147,7 @@ namespace l::nodegraph { } void GraphInputMidiKeyboard::NoteOn(int32_t note, int32_t velocity) { + mLastNote = note; float frequency = l::audio::GetFrequencyFromNote(static_cast(note)); mNode->SetInput(0, frequency); mNode->SetInput(1, velocity / 128.0f); @@ -139,6 +166,9 @@ namespace l::nodegraph { } void GraphInputMidiKeyboard::NoteOff(int32_t note) { + if (mLastNote == note) { + mNode->SetInput(0, 0.0f); + } auto input3 = &mNode->GetInput(3, 8); for (int32_t i = 0; i < 8; i++) { if (l::math::functions::equal(*input3, l::audio::gNoNote_f)) { diff --git a/packages/nodegraph/source/common/NodeGraphOpSource.cpp b/packages/nodegraph/source/common/NodeGraphOpSource.cpp index 02402316..64eb49ca 100644 --- a/packages/nodegraph/source/common/NodeGraphOpSource.cpp +++ b/packages/nodegraph/source/common/NodeGraphOpSource.cpp @@ -77,8 +77,8 @@ namespace l::nodegraph { mNode->SetInput(4, 0.0f); mNode->SetInputBound(0, InputBound::INPUT_UNBOUNDED); mNode->SetInputBound(1, InputBound::INPUT_0_100); - mNode->SetInputBound(2, InputBound::INPUT_UNBOUNDED); - mNode->SetInputBound(3, InputBound::INPUT_UNBOUNDED); + mNode->SetInputBound(2, InputBound::INPUT_0_TO_2); + mNode->SetInputBound(3, InputBound::INPUT_0_TO_1); mNode->SetInputBound(4, InputBound::INPUT_0_TO_1); } @@ -107,26 +107,47 @@ namespace l::nodegraph { mFmod = static_cast(inputs.at(2).Get()); mPmod = static_cast(inputs.at(3).Get()); + double fmodRange = 16.0; + mFmod = mFmod > 1.0 ? fmodRange * (mFmod - 1.0) : 1.0 / (1.0 + fmodRange * (1.0 - mFmod)); + //double limitFmMod = 1.0 / l::math::functions::max(mFreq / 25.0, 1.0); //mPmod = 800.0 * fmMod * fmMod * limitFmMod; - double limitFmMod = 1.0 / l::math::functions::max(mFreq / 25.0, 1.0); - mFmod = 800.0 * mFmod * mFmod * limitFmMod; + //double limitFmMod = 1.0 / l::math::functions::max(mFreq / 25.0, 1.0); + //mFmod = 800.0 * mFmod * mFmod * limitFmMod; for (int32_t i = start; i < end; i++) { double phaseDelta = mDeltaTime * mFreq; - mPhase += phaseDelta * (1.0 + mFmod); + mPhaseFmod += phaseDelta; + mPhaseFmod = l::math::functions::mod(mPhaseFmod, 1.0); + double modulation = mFmod * (1.0 + 0.5 * l::math::functions::sin(l::math::constants::PI * mPhaseFmod * 2.0)); + + mPhase += phaseDelta * modulation; + mPhase = l::math::functions::mod(mPhase, 1.0); + + double phaseMod = mPhase + mPmod; + phaseMod = l::math::functions::mod(phaseMod, 1.0); + + double sine = l::math::functions::sin(l::math::constants::PI * (mPhase + phaseMod)); + + + /* + double phaseDelta = mDeltaTime * mFreq; + mPhase += phaseDelta * mFmod; mPhase = l::math::functions::mod(mPhase, 1.0); - mPhaseMod += mPhase + mPmod; - mPhaseMod = l::math::functions::mod(mPhaseMod, 1.0); + double phaseMod = mPhase + mPmod; + phaseMod = l::math::functions::mod(phaseMod, 1.0); - double sine = l::math::functions::sin(l::math::constants::PI * (mPhase + mPhaseMod)); + double sine = l::math::functions::sin(l::math::constants::PI * (mPhase + phaseMod)); + */ mVol += (1.0f / 256.0f) * (mVolume - mVol); - *output0++ = mVol * static_cast(sine); + mWave += 0.25 * (sine - mWave); + + *output0++ = mVol * static_cast(mWave); } } ); @@ -337,4 +358,71 @@ namespace l::nodegraph { ); } + /*********************************************************************/ + void GraphSourceSaw::Reset() { + // { "Freq", "Volume", "Fmod", "Phase", "Reset"}; + mPhase = 0.0f; + mNode->SetInput(0, 0.0f); + mNode->SetInput(1, 0.0f); + mNode->SetInput(2, 1.0f); + mNode->SetInput(3, 0.0f); + mNode->SetInput(4, 0.0f); + mNode->SetInputBound(0, InputBound::INPUT_UNBOUNDED); + mNode->SetInputBound(1, InputBound::INPUT_0_100); + mNode->SetInputBound(2, InputBound::INPUT_0_TO_2); + mNode->SetInputBound(3, InputBound::INPUT_0_TO_1); + mNode->SetInputBound(4, InputBound::INPUT_0_TO_1); + } + + void GraphSourceSaw::Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) { + float* output0 = &outputs.at(0).GetOutput(numSamples); + + mSamplesUntilUpdate = l::audio::BatchUpdate(256.0f, mSamplesUntilUpdate, 0, numSamples, + [&]() { + mFreq = l::math::functions::max(static_cast(inputs.at(0).Get()), 0.0); + mVolume = inputs.at(1).Get(); + mReset = inputs.at(4).Get(); + + if (mFreq == 0.0f) { + mVolume = 0.0f; + outputs.at(0).mOutput = 0.0f; + return; + } + if (mReset > 0.5f) { + mVolume = 0.0f; + } + mDeltaTime = 1.0 / 44100.0; + + }, + [&](int32_t start, int32_t end, bool) { + + mFmod = static_cast(inputs.at(2).Get()); + mPmod = static_cast(inputs.at(3).Get()); + + double fmodRange = 16.0; + mFmod = mFmod > 1.0 ? fmodRange * (mFmod - 1.0) : 1.0 / (1.0 + fmodRange * (1.0 - mFmod)); + + for (int32_t i = start; i < end; i++) { + double phaseDelta = mDeltaTime * mFreq; + + mPhaseFmod += phaseDelta; + mPhaseFmod = l::math::functions::mod(mPhaseFmod, 1.0); + double modulation = mFmod * mPhaseFmod; + + mPhase += phaseDelta * modulation; + mPhase = l::math::functions::mod(mPhase, 1.0); + + double phaseMod = mPhase + mPmod; + phaseMod = l::math::functions::mod(phaseMod, 1.0); + + double saw = mPhase + phaseMod - 1.0; + + mVol += (1.0f / 256.0f) * (mVolume - mVol); + + mWave += 0.25 * (saw - mWave); + *output0++ = mVol * static_cast(mWave); + } + } + ); + } } diff --git a/packages/nodegraph/source/common/NodeGraphSchema.cpp b/packages/nodegraph/source/common/NodeGraphSchema.cpp index 17f3bfef..3a08019f 100644 --- a/packages/nodegraph/source/common/NodeGraphSchema.cpp +++ b/packages/nodegraph/source/common/NodeGraphSchema.cpp @@ -52,6 +52,9 @@ namespace l::nodegraph { case 8: node = mMainNodeGraph.NewNode(OutputType::Default); break; + case 9: + node = mMainNodeGraph.NewNode(OutputType::Default); + break; case 50: node = mMainNodeGraph.NewNode(OutputType::Default); break; @@ -98,7 +101,7 @@ namespace l::nodegraph { node = mMainNodeGraph.NewNode(OutputType::ExternalVisualOutput, 100); break; case 250: - node = mMainNodeGraph.NewNode(OutputType::Default); + node = mMainNodeGraph.NewNode(OutputType::Default); break; case 251: node = mMainNodeGraph.NewNode(OutputType::Default); From 30698d98726bf302632f991e54113a34231bb2b4 Mon Sep 17 00:00:00 2001 From: lnd3 Date: Tue, 10 Sep 2024 18:24:54 +0200 Subject: [PATCH 10/42] Add support for multiple midi devices. Fix some init bugs. Extend midi interface. --- packages/hid/include/hid/KeyboardPiano.h | 3 +- packages/hid/include/hid/Midi.h | 51 +++- packages/hid/source/common/Midi.cpp | 37 ++- packages/hid/source/linux/MidiLinux.cpp | 2 + packages/hid/source/linux/MidiLinux.h | 1 + packages/hid/source/windows/MidiWindows.cpp | 220 +++++++++++++----- packages/hid/source/windows/MidiWindows.h | 19 +- .../nodegraph/include/nodegraph/NodeGraph.h | 3 + .../include/nodegraph/NodeGraphOpInput.h | 127 ++++++++-- .../nodegraph/source/common/NodeGraph.cpp | 8 + .../source/common/NodeGraphOpInput.cpp | 212 +++++++++++++---- 11 files changed, 527 insertions(+), 156 deletions(-) diff --git a/packages/hid/include/hid/KeyboardPiano.h b/packages/hid/include/hid/KeyboardPiano.h index dd1a88bf..30ad4873 100644 --- a/packages/hid/include/hid/KeyboardPiano.h +++ b/packages/hid/include/hid/KeyboardPiano.h @@ -13,9 +13,10 @@ namespace l::hid { class INoteProcessor { public: virtual ~INoteProcessor() = default; - virtual void NoteOn(int32_t) {} + virtual void NoteOn(int32_t, int32_t = 127) {} virtual void NoteOff() {} virtual void NoteOff(int32_t) {} + virtual void NoteSustain(bool) {} }; enum class KeyFunctionTypes { diff --git a/packages/hid/include/hid/Midi.h b/packages/hid/include/hid/Midi.h index 352d45f6..46ca782c 100644 --- a/packages/hid/include/hid/Midi.h +++ b/packages/hid/include/hid/Midi.h @@ -10,7 +10,8 @@ namespace l::hid::midi { struct MidiData { uint32_t msg; // input message - uint32_t device; // device id + uint32_t deviceIn; // device id + uint32_t deviceOut; // corresponding out device uint32_t timestamp; // Since opening the midi device uint32_t status; // noteon=145, noteoff=129, knob=176, padon=144, padoff=128, sustain=177, uint32_t channel; @@ -19,6 +20,32 @@ namespace l::hid::midi { uint32_t unused; }; + + struct DeviceInfo { + std::string mName; + int32_t mInDevice = -1; + int32_t mOutDevice = -1; + int32_t mChannelKeys = -1; + int32_t mChannelButtons = -1; + int32_t mChannelKnobs = -1; + + std::string_view GetName() { + return mName; + } + bool HasMidiIn() { + return mInDevice >= 0; + } + bool HasMidiOut() { + return mInDevice >= 0; + } + uint32_t GetMidiIn() { + return static_cast(mInDevice); + } + uint32_t GetMidiOut() { + return static_cast(mOutDevice); + } + }; + /* uint32_t msg; // message uint32_t instance; // device id @@ -30,26 +57,34 @@ namespace l::hid::midi { */ using CallbackFunction = std::function; + struct MidiHandle { + int32_t mCallbackId = 0; + uint32_t mMidiDeviceInId = 0; + uint32_t mMidiDeviceOutId = 0; + }; + namespace details { extern std::mutex midiCallbackMutex; extern int32_t midiGuid; extern std::unordered_map midiCallback; - void HandleMidiData(uint32_t msg, uint32_t instance, uint32_t param1, uint32_t param2); + void HandleMidiData(uint32_t msg, uint32_t deviceIn, uint32_t deviceOut, uint32_t param1, uint32_t param2); } class MidiManager { public: - MidiManager() { - RegisterCallback([](const MidiData& data) { - LOG(LogInfo) << "listener 1: dev" << data.device << " stat " << data.status << " ch " << data.channel << " d1 " << data.data1 << " d2 " << data.data2; - }); - } - virtual ~MidiManager() = default; + MidiManager() = default; + virtual ~MidiManager() { + ClearCallbacks(); + }; + virtual void ClearCallbacks(); + virtual void RescanMidiDevices(); virtual int32_t RegisterCallback(CallbackFunction cb); virtual void UnregisterCallback(int32_t id); virtual uint32_t GetNumDevices(); + virtual std::string_view GetDeviceName(uint32_t); + virtual DeviceInfo* GetDeviceInfo(uint32_t deviceId); virtual void SendToDevice(uint32_t deviceIndex, uint32_t status, uint32_t channel, uint32_t data1, uint32_t data2); }; diff --git a/packages/hid/source/common/Midi.cpp b/packages/hid/source/common/Midi.cpp index 82d5358c..178f1994 100644 --- a/packages/hid/source/common/Midi.cpp +++ b/packages/hid/source/common/Midi.cpp @@ -11,28 +11,28 @@ namespace l::hid::midi { int32_t midiGuid = 0; std::unordered_map midiCallback; - void HandleMidiData(uint32_t msg, uint32_t instance, uint32_t param1, uint32_t param2) { + void HandleMidiData(uint32_t msg, uint32_t deviceId, uint32_t deviceOutId, uint32_t param1, uint32_t param2) { switch (msg) { case MIM_OPEN: - LOG(LogInfo) << "MIM_OPEN"; + //LOG(LogInfo) << "MIM_OPEN"; return; case MIM_CLOSE: - LOG(LogInfo) << "MIM_CLOSE"; + //LOG(LogInfo) << "MIM_CLOSE"; return; case MIM_LONGDATA: - LOG(LogInfo) << "Long data: " << msg << " " << instance << " " << param1 << " " << param2; + LOG(LogInfo) << "Long data: " << msg << " " << deviceId << " " << param1 << " " << param2; return; case MIM_ERROR: - LOG(LogInfo) << "Error: " << msg << " " << instance << " " << param1 << " " << param2; + LOG(LogInfo) << "Error: " << msg << " " << deviceId << " " << param1 << " " << param2; return; case MIM_LONGERROR: - LOG(LogInfo) << "Long error: " << msg << " " << instance << " " << param1 << " " << param2; + LOG(LogInfo) << "Long error: " << msg << " " << deviceId << " " << param1 << " " << param2; return; case MIM_MOREDATA: - LOG(LogInfo) << "More data: " << msg << " " << instance << " " << param1 << " " << param2; + LOG(LogInfo) << "More data: " << msg << " " << deviceId << " " << param1 << " " << param2; return; case MIM_DATA: - //LOG(LogInfo) << "Data: " << msg << " " << instance << " " << param1 << " " << param2; + //LOG(LogInfo) << "Data: " << msg << " " << deviceId << " " << param1 << " " << param2; break; default: LOG(LogInfo) << "default"; @@ -43,10 +43,11 @@ namespace l::hid::midi { if (!midiCallback.empty()) { MidiData data; data.msg = msg; - data.device = instance; + data.deviceIn = deviceId; + data.deviceOut = deviceOutId; data.timestamp = param2; - data.status = (param1 >> 4) & 0xf; data.channel = (param1) & 0xf; + data.status = (param1 >> 4) & 0xf; data.data1 = (param1 >> 8) & 0xff; data.data2 = (param1 >> 16) & 0xff; data.unused = 0; @@ -60,9 +61,17 @@ namespace l::hid::midi { } } + void MidiManager::ClearCallbacks() { + details::midiCallback.clear(); + } + + void MidiManager::RescanMidiDevices() {} + int32_t MidiManager::RegisterCallback(CallbackFunction f) { std::lock_guard lock(details::midiCallbackMutex); int32_t id = details::midiGuid++; + + details::midiCallback.emplace(id, f); return id; } @@ -75,6 +84,14 @@ namespace l::hid::midi { return 0; } + std::string_view MidiManager::GetDeviceName(uint32_t) { + return ""; + } + + DeviceInfo* MidiManager::GetDeviceInfo(uint32_t) { + return nullptr; + } + void MidiManager::SendToDevice(uint32_t, uint32_t, uint32_t, uint32_t, uint32_t) { } } diff --git a/packages/hid/source/linux/MidiLinux.cpp b/packages/hid/source/linux/MidiLinux.cpp index bd629dca..28bc09dc 100644 --- a/packages/hid/source/linux/MidiLinux.cpp +++ b/packages/hid/source/linux/MidiLinux.cpp @@ -8,6 +8,8 @@ namespace l::hid::midi { return nullptr; } + void MidiManagerLinux::RescanMidiDevices() {} + uint32_t MidiManagerLinux::GetNumDevices() { return 0; } diff --git a/packages/hid/source/linux/MidiLinux.h b/packages/hid/source/linux/MidiLinux.h index 5581792c..203cff56 100644 --- a/packages/hid/source/linux/MidiLinux.h +++ b/packages/hid/source/linux/MidiLinux.h @@ -10,6 +10,7 @@ namespace l::hid::midi { public: MidiManagerLinux() = default; + virtual void RescanMidiDevices() override; virtual uint32_t GetNumDevices() override; virtual void SendToDevice(uint32_t deviceIndex, uint32_t status, uint32_t channel, uint32_t data1, uint32_t data2) override; protected: diff --git a/packages/hid/source/windows/MidiWindows.cpp b/packages/hid/source/windows/MidiWindows.cpp index dd6126a3..628ccdc5 100644 --- a/packages/hid/source/windows/MidiWindows.cpp +++ b/packages/hid/source/windows/MidiWindows.cpp @@ -9,23 +9,29 @@ namespace l::hid::midi { } namespace details { - void CALLBACK MidiInProc(HMIDIIN, UINT wMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2) { - HandleMidiData(static_cast(wMsg), static_cast(dwInstance), static_cast(dwParam1), static_cast(dwParam2)); + void CALLBACK MidiInProc(HMIDIIN deviceIn, UINT wMsg, DWORD_PTR dwInstance, DWORD dwParam1, DWORD dwParam2) { + auto midiPtr = reinterpret_cast(dwInstance); + auto deviceInfo = midiPtr->getDeviceInfo(deviceIn); + if (deviceInfo) { + HandleMidiData(static_cast(wMsg), deviceInfo->GetMidiIn(), deviceInfo->GetMidiOut(), static_cast(dwParam1), static_cast(dwParam2)); + } } - void CALLBACK MidiOutProc(HMIDIIN, UINT wMsg, DWORD, DWORD, DWORD) { + void CALLBACK MidiOutProc(HMIDIOUT, UINT wMsg, DWORD_PTR, DWORD, DWORD) { + //auto midiPtr = reinterpret_cast(dwInstance); + //auto id = midiPtr->getDeviceOutId(deviceOut); switch (wMsg) { case MOM_OPEN: - LOG(LogInfo) << "MOM_OPEN"; + //LOG(LogInfo) << "MOM_OPEN"; break; case MOM_CLOSE: - LOG(LogInfo) << "MOM_CLOSE"; + //LOG(LogInfo) << "MOM_CLOSE"; break; case MOM_DONE: - LOG(LogInfo) << "MOM_DONE"; + //LOG(LogInfo) << "MOM_DONE"; break; default: - LOG(LogInfo) << "Something else.."; + //LOG(LogInfo) << "Something else.."; break; } @@ -33,61 +39,6 @@ namespace l::hid::midi { } Midi::Midi() : mHeader{}, mMidiBuffer{} { - UINT nMidiDeviceNum = midiInGetNumDevs(); - if (nMidiDeviceNum == 0) { - return; - } - - UINT nMidiOutDeviceNum = midiOutGetNumDevs(); - - LOG(LogInfo) << "Number of midi in devices: " << nMidiDeviceNum; - LOG(LogInfo) << "Number of midi out devices: " << nMidiOutDeviceNum; - - for (size_t deviceId = 0; deviceId < nMidiDeviceNum; deviceId++) { - MMRESULT rv; - HMIDIIN hMidiDevice = NULL; - - rv = midiInOpen(&hMidiDevice, static_cast(deviceId), reinterpret_cast(&details::MidiInProc), 0, CALLBACK_FUNCTION); - if (rv == MMSYSERR_ALLOCATED) { - return; - } - if (rv != MMSYSERR_NOERROR) { - LOG(LogError) << "Failed to open midi in device " << deviceId; - continue; - } - - rv = midiInStart(hMidiDevice); - if (rv != MMSYSERR_NOERROR) { - LOG(LogError) << "Failed to start midi in device" << deviceId; - continue; - } - - MIDIINCAPS capsIn; - rv = midiInGetDevCaps(deviceId, &capsIn, sizeof(MIDIINCAPS)); - if (rv != MMSYSERR_NOERROR) { - LOG(LogError) << "Failed to get midi in caps on device " << deviceId; - } - - HMIDIOUT hMidiDeviceOut = nullptr; - deviceId++; - rv = midiOutOpen(&hMidiDeviceOut, static_cast(deviceId), reinterpret_cast(&details::MidiOutProc), 0, CALLBACK_FUNCTION); - if (rv == MMSYSERR_ALLOCATED) { - return; - } - if (rv != MMSYSERR_NOERROR) { - LOG(LogError) << "Failed to open midi out device " << deviceId; - } - - MIDIOUTCAPS capsOut; - rv = midiOutGetDevCaps(deviceId, &capsOut, sizeof(MIDIOUTCAPS)); - if (rv != MMSYSERR_NOERROR) { - LOG(LogError) << "Failed to get midi out caps on device " << deviceId; - } - deviceId--; - - devices.push_back(std::make_pair(hMidiDevice, hMidiDeviceOut)); - caps.push_back(std::make_pair(capsIn, capsOut)); - } } Midi::~Midi() { @@ -113,6 +64,107 @@ namespace l::hid::midi { } } + void Midi::initDevices() { + mDeviceInfo.clear(); + devices.clear(); + caps.clear(); + + UINT nMidiInDeviceInNum = midiInGetNumDevs(); + if (nMidiInDeviceInNum == 0) { + return; + } + + UINT nMidiOutDeviceNum = midiOutGetNumDevs(); + + LOG(LogInfo) << "Number of midi in devices: " << nMidiInDeviceInNum; + LOG(LogInfo) << "Number of midi out devices: " << nMidiOutDeviceNum; + + for (uint32_t deviceId = 0; deviceId < nMidiInDeviceInNum || deviceId < nMidiOutDeviceNum; deviceId++) { + MMRESULT rv; + HMIDIIN hMidiDeviceIn = nullptr; + HMIDIOUT hMidiDeviceOut = nullptr; + MIDIINCAPS capsIn = { 0 }; + MIDIOUTCAPS capsOut = { 0 }; + + if (deviceId < nMidiInDeviceInNum) { + rv = midiInGetDevCaps(deviceId, &capsIn, sizeof(MIDIINCAPS)); + if (rv != MMSYSERR_NOERROR) { + LOG(LogError) << "Failed to get midi in caps on device " << deviceId; + } + LOG(LogInfo) << "Midi in device id " << deviceId << " : " << capsIn.szPname << ", support : " << capsIn.dwSupport << ", pid : " << capsIn.wPid; + + rv = midiInOpen(&hMidiDeviceIn, static_cast(deviceId), reinterpret_cast(&details::MidiInProc), (DWORD_PTR)(this), CALLBACK_FUNCTION | MIDI_IO_STATUS); + if (rv == MMSYSERR_ALLOCATED) { + return; + } + if (rv != MMSYSERR_NOERROR) { + LOG(LogError) << "Failed to open midi in device " << deviceId; + continue; + } + + rv = midiInStart(hMidiDeviceIn); + if (rv != MMSYSERR_NOERROR) { + LOG(LogError) << "Failed to start midi in device" << deviceId; + continue; + } + } + + if (deviceId < nMidiOutDeviceNum) { + rv = midiOutGetDevCaps(deviceId, &capsOut, sizeof(MIDIOUTCAPS)); + if (rv != MMSYSERR_NOERROR) { + LOG(LogError) << "Failed to get midi out caps on device " << deviceId; + } + LOG(LogInfo) << "Midi out device id " << deviceId << " : " << capsOut.szPname << ", support : " << capsOut.dwSupport << ", pid : " << capsOut.wPid; + + rv = midiOutOpen(&hMidiDeviceOut, static_cast(deviceId), reinterpret_cast(&details::MidiOutProc), (DWORD_PTR)(this), CALLBACK_FUNCTION); + if (rv == MMSYSERR_ALLOCATED) { + return; + } + if (rv != MMSYSERR_NOERROR) { + LOG(LogError) << "Failed to open midi out device " << deviceId; + } + } + + devices.push_back(std::make_pair(hMidiDeviceIn, hMidiDeviceOut)); + caps.push_back(std::make_pair(capsIn, capsOut)); + + if (deviceId + 1 >= mDeviceInfo.size()) { + mDeviceInfo.resize(deviceId + 1); + } + if (hMidiDeviceIn != nullptr) { + auto& deviceInfo = mDeviceInfo.at(deviceId); + deviceInfo.mInDevice = static_cast(deviceId); + if (capsIn.szPname) { + deviceInfo.mName = capsIn.szPname; + + if (deviceInfo.mName == "APC Key 25") { + deviceInfo.mChannelKeys = 1; + deviceInfo.mChannelButtons = 0; + deviceInfo.mChannelKnobs = 0; + } + else if (deviceInfo.mName == "Keystation Mini 32") { + deviceInfo.mChannelKeys = 0; + deviceInfo.mChannelButtons = 0; + deviceInfo.mChannelKnobs = 0; + } + else { + deviceInfo.mChannelKeys = 0; + deviceInfo.mChannelButtons = 0; + deviceInfo.mChannelKnobs = 0; + } + } + } + if (hMidiDeviceOut != nullptr) { + for (int32_t i = 0; i < mDeviceInfo.size(); i++) { + auto& deviceInfo = mDeviceInfo.at(i); + if (std::string(capsOut.szPname) == deviceInfo.GetName()) { + deviceInfo.mOutDevice = static_cast(deviceId); + } + } + } + } + } + uint32_t Midi::getNumDevices() { return static_cast(devices.size()); } @@ -223,10 +275,54 @@ namespace l::hid::midi { } } + DeviceInfo* Midi::getDeviceInfo(HMIDIIN midiIn) { + for (int32_t i = 0; i < devices.size() && mDeviceInfo.size(); i++) { + if (devices.at(i).first == midiIn) { + return &mDeviceInfo.at(i); + } + } + return nullptr; + } + + DeviceInfo* Midi::getDeviceInfo(uint32_t deviceId) { + for (int32_t i = 0; i < mDeviceInfo.size(); i++) { + auto& deviceInfo = mDeviceInfo.at(i); + if (deviceInfo.GetMidiIn() == deviceId) { + return &deviceInfo; + } + } + return 0; + } + + MidiManagerWindows::MidiManagerWindows() { + RescanMidiDevices(); + } + + void MidiManagerWindows::RescanMidiDevices() { + ClearCallbacks(); + mMidiDevice.initDevices(); + + RegisterCallback([](const MidiData& data) { + LOG(LogInfo) << "midi cb: device in:" << data.deviceIn << " device out:" << data.deviceOut << " stat:" << data.status << " ch:" << data.channel << " d1:" << data.data1 << " d2:" << data.data2; + }); + } + uint32_t MidiManagerWindows::GetNumDevices() { return mMidiDevice.getNumDevices(); } + std::string_view MidiManagerWindows::GetDeviceName(uint32_t deviceId) { + auto deviceInfo = mMidiDevice.getDeviceInfo(deviceId); + if (deviceInfo != nullptr) { + return deviceInfo->GetName(); + } + return ""; + } + + DeviceInfo* MidiManagerWindows::GetDeviceInfo(uint32_t deviceId) { + return mMidiDevice.getDeviceInfo(deviceId); + } + void MidiManagerWindows::SendToDevice(uint32_t deviceIndex, uint32_t status, uint32_t channel, uint32_t data1, uint32_t data2) { mMidiDevice.send(deviceIndex, status, channel, data1, data2); } diff --git a/packages/hid/source/windows/MidiWindows.h b/packages/hid/source/windows/MidiWindows.h index 99834a3b..0855ee8d 100644 --- a/packages/hid/source/windows/MidiWindows.h +++ b/packages/hid/source/windows/MidiWindows.h @@ -11,8 +11,8 @@ namespace l::hid::midi { namespace details { - void CALLBACK MidiInProc(HMIDIIN, UINT wMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2); - void CALLBACK MidiOutProc(HMIDIIN, UINT wMsg, DWORD, DWORD, DWORD); + void CALLBACK MidiInProc(HMIDIIN, UINT wMsg, DWORD_PTR dwInstance, DWORD dwParam1, DWORD dwParam2); + void CALLBACK MidiOutProc(HMIDIOUT, UINT wMsg, DWORD_PTR, DWORD, DWORD); } class Midi { @@ -20,27 +20,34 @@ namespace l::hid::midi { Midi(); ~Midi(); + void initDevices(); uint32_t getNumDevices(); void clearBuffer(); void pushBuffer(unsigned char byte); void sendSysex2(uint32_t deviceId, uint32_t i, uint32_t data); void send(uint32_t deviceId, uint32_t status, uint32_t channel, uint32_t data1, uint32_t data2); - + + DeviceInfo* getDeviceInfo(HMIDIIN midiIn); + DeviceInfo* getDeviceInfo(uint32_t deviceId); private: - MIDIHDR mHeader{}; + MIDIHDR mHeader; char mMidiBuffer[16]; CallbackFunction mCallback; std::vector> devices; - std::vector< std::pair> caps; + std::vector> caps; + std::vector mDeviceInfo; }; class MidiManagerWindows : public MidiManager { public: - MidiManagerWindows() = default; + MidiManagerWindows(); + virtual void RescanMidiDevices() override; virtual uint32_t GetNumDevices() override; + virtual std::string_view GetDeviceName(uint32_t deviceId) override; + virtual DeviceInfo* GetDeviceInfo(uint32_t deviceId) override; virtual void SendToDevice(uint32_t deviceIndex, uint32_t status, uint32_t channel, uint32_t data1, uint32_t data2) override; protected: Midi mMidiDevice; diff --git a/packages/nodegraph/include/nodegraph/NodeGraph.h b/packages/nodegraph/include/nodegraph/NodeGraph.h index ee04b7a9..35a5b999 100644 --- a/packages/nodegraph/include/nodegraph/NodeGraph.h +++ b/packages/nodegraph/include/nodegraph/NodeGraph.h @@ -112,6 +112,8 @@ namespace l::nodegraph { int8_t mInputFromOutputChannel = 0; std::unique_ptr mName; + + // hack to get input buffers working std::unique_ptr> mInputBuf = nullptr; void Reset(); @@ -164,6 +166,7 @@ namespace l::nodegraph { virtual bool SetInput(int8_t inputChannel, NodeGraphGroup& source, int8_t sourceOutputChannel); virtual bool SetInput(int8_t inputChannel, float constant, int32_t size = -1); virtual bool SetInput(int8_t inputChannel, float* floatPtr); + virtual void SetDefaultOutput(int8_t outputChannel, float constant, int32_t size = -1); virtual bool SetInputBound(int8_t inputChannel, InputBound bound = InputBound::INPUT_DONTCHANGE, float boundMin = 0.0f, float boundMax = 0.0f); virtual bool RemoveInput(void* source); diff --git a/packages/nodegraph/include/nodegraph/NodeGraphOpInput.h b/packages/nodegraph/include/nodegraph/NodeGraphOpInput.h index bd1e8aa9..4d527b7d 100644 --- a/packages/nodegraph/include/nodegraph/NodeGraphOpInput.h +++ b/packages/nodegraph/include/nodegraph/NodeGraphOpInput.h @@ -48,9 +48,10 @@ namespace l::nodegraph { virtual bool IsDataVisible(int8_t) override { return true; } - virtual void NoteOn(int32_t note) override; + virtual void NoteOn(int32_t note, int32_t velocity) override; virtual void NoteOff() override; virtual void NoteOff(int32_t note) override; + virtual void NoteSustain(bool on) override; protected: int8_t ResetNoteChannel(int32_t note); int8_t GetNextNoteChannel(int32_t note); @@ -62,79 +63,128 @@ namespace l::nodegraph { }; /*********************************************************************/ - class GraphInputMidiKeyboard : public NodeGraphOp { + class GraphInputMidiKeyboard : public NodeGraphOp, public l::hid::INoteProcessor { public: GraphInputMidiKeyboard(NodeGraphBase* node, l::hid::midi::MidiManager* midiManager) : - NodeGraphOp(node, 0, 4, 4), + NodeGraphOp(node, 0, 5, 5), mMidiManager(midiManager) { mChannel.resize(1); mMidiManager->RegisterCallback([&](l::hid::midi::MidiData data) { - MidiEvent(data); + if (mMidiDeviceInId >= 0 && data.deviceIn == static_cast(mMidiDeviceInId)) { + MidiEvent(data); + } }); + + SetDevice(0); } - std::string defaultOutStrings[4] = { "Freq", "Velocity", "Note On Id", "Note Off Id"}; + std::string defaultOutStrings[5] = { "Freq", "Velocity", "Note On Id", "Note Off Id", "Device Id"}; virtual ~GraphInputMidiKeyboard() = default; - virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; virtual void Reset() override; + virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; + virtual void Tick(int32_t tick, float deltaTime) override; virtual std::string_view GetOutputName(int8_t outputChannel) override { return defaultOutStrings[outputChannel]; } virtual std::string_view GetName() override { - return "Midi Device"; + return mNodeName; } virtual bool IsDataVisible(int8_t) override { return true; } + virtual bool IsDataEditable(int8_t channel) override { + return channel >= 4 ? true : false; + } virtual void MidiEvent(const l::hid::midi::MidiData& data); - void NoteOn(int32_t note, int32_t velocity); - void NoteOff(); - void NoteOff(int32_t note); + virtual void NoteOn(int32_t note, int32_t velocity = 127) override; + virtual void NoteOff() override; + virtual void NoteOff(int32_t note) override; + virtual void NoteSustain(bool on) override; protected: int8_t ResetNoteChannel(int32_t note); int8_t GetNextNoteChannel(int32_t note); + void SetDevice(int32_t deviceId) { + if (deviceId >= 0 && mMidiDeviceInId != deviceId) { + auto deviceInfo = mMidiManager->GetDeviceInfo(static_cast(deviceId)); + if (deviceInfo) { + mMidiDeviceInId = deviceId; + mMidiChannelKeys = deviceInfo->mChannelKeys; + mNodeName = deviceInfo->GetName(); + mNodeName += " : Midi"; + } + } + } + l::hid::midi::MidiManager* mMidiManager = nullptr; int32_t mLastNote = 0; int8_t mNoteCounter = 0; + bool mSustain = false; std::vector> mChannel; + int32_t mMidiDeviceInId = -1; + int32_t mMidiChannelKeys = -1; + std::string mNodeName; + std::vector mSustainedNotes; }; /*********************************************************************/ class GraphInputMidiKnobs : public NodeGraphOp { public: GraphInputMidiKnobs(NodeGraphBase* node, l::hid::midi::MidiManager* midiManager) : - NodeGraphOp(node, 0, 8, 8), + NodeGraphOp(node, 0, 9, 9), mMidiManager(midiManager) { mMidiManager->RegisterCallback([&](l::hid::midi::MidiData data) { - MidiEvent(data); + if (mMidiDeviceInId >= 0 && data.deviceIn == static_cast(mMidiDeviceInId)) { + MidiEvent(data); + } }); + + SetDevice(0); } - std::string defaultOutStrings[8] = { "Knob 1", "Knob 2", "Knob 3", "Knob 4", "Knob 5", "Knob 6", "Knob 7", "Knob 8", }; + std::string defaultOutStrings[9] = { "Knob 1", "Knob 2", "Knob 3", "Knob 4", "Knob 5", "Knob 6", "Knob 7", "Knob 8", "Device Id"}; virtual ~GraphInputMidiKnobs() = default; - virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; virtual void Reset() override; + virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; + virtual void Tick(int32_t tick, float deltaTime) override; virtual std::string_view GetOutputName(int8_t outputChannel) override { return defaultOutStrings[outputChannel]; } virtual std::string_view GetName() override { - return "Midi Knobs"; + return mNodeName; } virtual bool IsDataVisible(int8_t) override { return true; } + virtual bool IsDataEditable(int8_t channel) override { + return channel >= 8 ? true : false; + } virtual void MidiEvent(const l::hid::midi::MidiData& data); protected: + void SetDevice(int32_t deviceId) { + if (deviceId >= 0 && mMidiDeviceInId != deviceId) { + auto deviceInfo = mMidiManager->GetDeviceInfo(static_cast(deviceId)); + if (deviceInfo) { + mMidiDeviceInId = deviceId; + mMidiChannelKnobs = deviceInfo->mChannelKnobs; + mNodeName = deviceInfo->GetName(); + mNodeName += " : Knobs"; + } + } + } + l::hid::midi::MidiManager* mMidiManager = nullptr; + int32_t mMidiDeviceInId = -1; + int32_t mMidiChannelKnobs = -1; + std::string mNodeName; }; /*********************************************************************/ @@ -171,21 +221,22 @@ namespace l::nodegraph { const std::vector remapToButtonStatesToColor = {0, 6, 3, 5, 1, 4, 2}; GraphInputMidiButtons(NodeGraphBase* node, l::hid::midi::MidiManager* midiManager, int32_t buttonGroup) : - NodeGraphOp(node, 0, 8, 8), + NodeGraphOp(node, 0, 9, 9), mMidiManager(midiManager), mButtonGroup(buttonGroup) { - mNodeName = "Midi Button Group "; - mNodeName += std::to_string(mButtonGroup); mButtonStates.resize(8); for (int8_t i = 0; i < 8; i++) { defaultOutStrings[i] = std::string("Pad ") + std::to_string(i); } + defaultOutStrings[8] = "Device Id"; if (mMidiManager) { mCallbackId = mMidiManager->RegisterCallback([&](l::hid::midi::MidiData data) { - MidiEvent(data); + if (mMidiDeviceInId >= 0 && data.deviceIn == static_cast(mMidiDeviceInId)) { + MidiEvent(data); + } }); // turn all buttons on @@ -193,9 +244,11 @@ namespace l::nodegraph { UpdateButton(i, BUTTON_ALLOCATED); } } + + SetDevice(0); } - std::string defaultOutStrings[8]; + std::string defaultOutStrings[9]; virtual ~GraphInputMidiButtons() { // turn off all buttons before destructing @@ -219,17 +272,49 @@ namespace l::nodegraph { virtual bool IsDataVisible(int8_t) override { return true; } + virtual bool IsDataEditable(int8_t channel) override { + return channel >= 8 ? true : false; + } + void UpdateButton(int8_t buttonId, int8_t buttonState) { if (mMidiManager) { int8_t buttonColor = remapToButtonStatesToColor.at(buttonState); - mMidiManager->SendToDevice(0, 0x90, 0, mButtonGroup * 8 + buttonId, buttonColor); + auto deviceInfo = mMidiManager->GetDeviceInfo(mMidiDeviceInId); + if (deviceInfo) { + mMidiManager->SendToDevice(deviceInfo->GetMidiOut(), 0x90, 0, mButtonGroup * 8 + buttonId, buttonColor); + } mButtonStates.at(buttonId) = buttonState; } } + void UpdateCommonButton(uint32_t buttonId, uint32_t buttonState) { + if (mMidiManager) { + auto deviceInfo = mMidiManager->GetDeviceInfo(mMidiDeviceInId); + if (deviceInfo) { + mMidiManager->SendToDevice(deviceInfo->GetMidiOut(), 0x90, 0, buttonId, buttonState); + } + } + } + virtual void MidiEvent(const l::hid::midi::MidiData& data); protected: + void SetDevice(int32_t deviceId) { + if (deviceId >= 0 && mMidiDeviceInId != deviceId) { + auto deviceInfo = mMidiManager->GetDeviceInfo(static_cast(deviceId)); + if (deviceInfo) { + mMidiDeviceInId = deviceId; + mMidiChannelButtons = deviceInfo->mChannelButtons; + mNodeName = deviceInfo->GetName(); + mNodeName += " : Pads"; + mNodeName += std::to_string(mButtonGroup); + } + } + } + l::hid::midi::MidiManager* mMidiManager = nullptr; + int32_t mMidiDeviceInId = -1; + int32_t mMidiChannelButtons = -1; + int32_t mCallbackId = 0; int32_t mButtonGroup; std::string mNodeName; diff --git a/packages/nodegraph/source/common/NodeGraph.cpp b/packages/nodegraph/source/common/NodeGraph.cpp index 95a099e1..e6b27df3 100644 --- a/packages/nodegraph/source/common/NodeGraph.cpp +++ b/packages/nodegraph/source/common/NodeGraph.cpp @@ -185,6 +185,7 @@ namespace l::nodegraph { for (int32_t i = 0; i < size; i++) { *inputBuf++ = constant; } + input.mInput.mInputFloatConstant = constant; } return true; @@ -202,6 +203,13 @@ namespace l::nodegraph { return true; } + void NodeGraphBase::SetDefaultOutput(int8_t outputChannel, float defaultValue, int32_t size) { + auto output = &GetOutput(outputChannel, size); + for (int32_t i = 0; i < size; i++) { + *output++ = defaultValue; + } + } + bool NodeGraphBase::SetInputBound(int8_t inputChannel, InputBound bound, float boundMin, float boundMax) { ASSERT(inputChannel >= 0 && static_cast(inputChannel) < mInputs.size()); if (!IsValidInOutNum(inputChannel, mInputs.size())) { diff --git a/packages/nodegraph/source/common/NodeGraphOpInput.cpp b/packages/nodegraph/source/common/NodeGraphOpInput.cpp index 11858fcd..618fa266 100644 --- a/packages/nodegraph/source/common/NodeGraphOpInput.cpp +++ b/packages/nodegraph/source/common/NodeGraphOpInput.cpp @@ -10,6 +10,15 @@ namespace l::nodegraph { /*********************************************************************/ + void GraphInputKeyboardPiano::Reset() { + mNode->SetInput(0, 0.0f); + mNode->SetInput(1, l::audio::gNoNote_f, 8); + mNode->SetInput(2, l::audio::gNoNote_f, 8); + + mNode->GetOutput(1, 8); + mNode->GetOutput(2, 8); + } + void GraphInputKeyboardPiano::Process(int32_t, std::vector& inputs, std::vector& outputs) { outputs.at(0).mOutput = inputs.at(0).Get(); @@ -29,13 +38,7 @@ namespace l::nodegraph { mKeyboard.Update(); } - void GraphInputKeyboardPiano::Reset() { - mNode->SetInput(0, 0.0f); - mNode->SetInput(1, l::audio::gNoNote_f, 8); - mNode->SetInput(2, l::audio::gNoNote_f, 8); - } - - void GraphInputKeyboardPiano::NoteOn(int32_t note) { + void GraphInputKeyboardPiano::NoteOn(int32_t note, int32_t) { mLastNote = note; float frequency = l::audio::GetFrequencyFromNote(static_cast(note)); mNode->SetInput(0, frequency); @@ -67,6 +70,9 @@ namespace l::nodegraph { } } + void GraphInputKeyboardPiano::NoteSustain(bool) { + } + int8_t GraphInputKeyboardPiano::ResetNoteChannel(int32_t note) { for (size_t i = 0; i < mChannel.size(); i++) { if (mChannel.at(i).first == note) { @@ -108,6 +114,17 @@ namespace l::nodegraph { } /*********************************************************************/ + void GraphInputMidiKeyboard::Reset() { + mNode->SetInput(0, 0.0f); + mNode->SetInput(1, 0.0f); + mNode->SetInput(2, l::audio::gNoNote_f, 8); + mNode->SetInput(3, l::audio::gNoNote_f, 8); + mNode->SetInput(4, 0.0f); + + mNode->GetOutput(2, 8); + mNode->GetOutput(3, 8); + } + void GraphInputMidiKeyboard::Process(int32_t, std::vector& inputs, std::vector& outputs) { outputs.at(0).mOutput = inputs.at(0).Get(); outputs.at(1).mOutput = inputs.at(1).Get(); @@ -122,28 +139,39 @@ namespace l::nodegraph { *input2++ = l::audio::gNoNote_f; *input3++ = l::audio::gNoNote_f; } + outputs.at(4).mOutput = l::math::functions::round(inputs.at(4).Get()); } - void GraphInputMidiKeyboard::Reset() { - mNode->SetInput(0, 0.0f); - mNode->SetInput(1, 0.0f); - mNode->SetInput(2, l::audio::gNoNote_f, 8); - mNode->SetInput(3, l::audio::gNoNote_f, 8); + void GraphInputMidiKeyboard::Tick(int32_t, float) { + auto deviceInId = static_cast(mNode->GetInput(4, 1) + 0.5f); + SetDevice(deviceInId); } void GraphInputMidiKeyboard::MidiEvent(const l::hid::midi::MidiData& data) { //LOG(LogInfo) << "listener 1: dev" << data.device << " stat " << data.status << " ch " << data.channel << " d1 " << data.data1 << " d2 " << data.data2; - if (data.channel != 1) { + if (mMidiChannelKeys < 0 || data.channel != static_cast(mMidiChannelKeys)) { return; } if (data.status == 9) { - // note on - NoteOn(data.data1, data.data2); + if (data.data2 > 0) { + // note on + NoteOn(data.data1, data.data2); + } + else { + // note off + NoteOff(data.data1); + } } else if (data.status == 8) { // note off NoteOff(data.data1); } + else if (data.status == 11) { + if (data.data1 == 64) { + // sustain + NoteSustain(data.data2 != 0); + } + } } void GraphInputMidiKeyboard::NoteOn(int32_t note, int32_t velocity) { @@ -152,6 +180,11 @@ namespace l::nodegraph { mNode->SetInput(0, frequency); mNode->SetInput(1, velocity / 128.0f); + if (mSustain && !mSustainedNotes.empty()) { + std::erase_if(mSustainedNotes, [&](const int32_t& sustainedNote) { + return sustainedNote == note; + }); + } auto input2 = &mNode->GetInput(2, 8); for (int32_t i = 0; i < 8; i++) { if (l::math::functions::equal(*input2, l::audio::gNoNote_f)) { @@ -162,20 +195,42 @@ namespace l::nodegraph { } } void GraphInputMidiKeyboard::NoteOff() { - Reset(); } void GraphInputMidiKeyboard::NoteOff(int32_t note) { if (mLastNote == note) { mNode->SetInput(0, 0.0f); } - auto input3 = &mNode->GetInput(3, 8); - for (int32_t i = 0; i < 8; i++) { - if (l::math::functions::equal(*input3, l::audio::gNoNote_f)) { - *input3 = static_cast(note); - break; + + if (!mSustain) { + auto input3 = &mNode->GetInput(3, 8); + for (int32_t i = 0; i < 8; i++) { + if (l::math::functions::equal(*input3, l::audio::gNoNote_f)) { + *input3 = static_cast(note); + break; + } + input3++; + } + } + else { + mSustainedNotes.push_back(note); + } + } + + void GraphInputMidiKeyboard::NoteSustain(bool on) { + mSustain = on; + if (!mSustain && !mSustainedNotes.empty()) { + for (auto note : mSustainedNotes) { + auto input3 = &mNode->GetInput(3, 8); + for (int32_t i = 0; i < 8; i++) { + if (l::math::functions::equal(*input3, l::audio::gNoNote_f)) { + *input3 = static_cast(note); + break; + } + input3++; + } } - input3++; + mSustainedNotes.clear(); } } @@ -220,34 +275,45 @@ namespace l::nodegraph { } /*********************************************************************/ + void GraphInputMidiKnobs::Reset() { + for (int8_t i = 0; i < GetNumInputs(); i++) { + mNode->SetInput(i, 0.0f); + } + mNode->SetInput(8, 0.0f); + } + void GraphInputMidiKnobs::Process(int32_t, std::vector& inputs, std::vector& outputs) { for (size_t i = 0; i < inputs.size(); i++) { outputs.at(i).mOutput = inputs.at(i).Get(); } } - void GraphInputMidiKnobs::Reset() { - for (int8_t i = 0; i < GetNumInputs(); i++) { - mNode->SetInput(i, 0.0f); - } + void GraphInputMidiKnobs::Tick(int32_t, float) { + auto deviceInId = static_cast(mNode->GetInput(8, 1) + 0.5f); + SetDevice(deviceInId); } void GraphInputMidiKnobs::MidiEvent(const l::hid::midi::MidiData& data) { - if (data.channel != 0) { + if (mMidiChannelKnobs < 0 || data.channel != static_cast(mMidiChannelKnobs)) { return; } if (data.status == 11) { - if (data.data1 >= 48 && data.data1 <= 55) { + if (data.data1 >= 48 && data.data1 < 56) { mNode->SetInput(static_cast(data.data1 - 48), data.data2 / 128.0f); } + else if (data.data1 >= 7 && data.data1 < 15) { + mNode->SetInput(static_cast(data.data1 - 7), data.data2 / 128.0f); + } } } + /*********************************************************************/ void GraphInputMidiButtons::Reset() { for (int8_t i = 0; i < GetNumInputs(); i++) { mNode->SetInput(i, 0.0f); } + mNode->SetInput(8, 0.0f); } void GraphInputMidiButtons::Process(int32_t, std::vector& inputs, std::vector& outputs) { @@ -257,6 +323,9 @@ namespace l::nodegraph { } void GraphInputMidiButtons::Tick(int32_t, float) { + auto deviceInId = static_cast(mNode->GetInput(8, 1) + 0.5f); + SetDevice(deviceInId); + if (mInitCounter == 120){ for (int8_t i = 0; i < 8; i++) { if (mButtonStates.at(i) == BUTTON_ALLOCATED) { @@ -269,30 +338,77 @@ namespace l::nodegraph { } void GraphInputMidiButtons::MidiEvent(const l::hid::midi::MidiData& data) { - if (data.channel != 0) { + if (mMidiChannelButtons < 0 || data.channel != static_cast(mMidiChannelButtons)) { return; } - if (data.data1 == 98) { - // shift button event - mMidiShiftState = data.status == 9 ? true : data.status == 8 ? false : mMidiShiftState; - } - - int8_t buttonIndex = static_cast(data.data1 - mButtonGroup * 8); - if (buttonIndex >= 0 && buttonIndex < 8) { - // pad button event - if (data.status == 9) { - if (mButtonStates.at(buttonIndex) <= BUTTON_ALLOCATED) { - // if button has not been polled before user input, set button to off first time - mButtonStates.at(buttonIndex) = BUTTON_OFF; - }else if (!mMidiShiftState && mButtonStates.at(buttonIndex) < BUTTON_ON) { - mButtonStates.at(buttonIndex)++; - }else if (mMidiShiftState && mButtonStates.at(buttonIndex) > BUTTON_OFF) { - mButtonStates.at(buttonIndex)--; + if (data.status == 9) { + if (data.data1 >= 64 && data.data1 <= 67) { + // navigation buttons up, down, left, right + // button colors none, red + UpdateCommonButton(data.data1, 1); + } + else if (data.data1 >= 68 && data.data1 <= 71) { + // navigation buttons volume, pan, send, device + // button colors none, red + UpdateCommonButton(data.data1, 1); + } + else if (data.data1 >= 81 && data.data1 <= 86) { + // navigation buttons stops all clips, clip stop, solo, rec arm, mute, select + // button colors none, green + UpdateCommonButton(data.data1, 1); + } + else if (data.data1 >= 91 && data.data1 <= 93) { + // navigation buttons play/pause, rec + // no buttons colors + } + else if (data.data1 == 98) { + // shift button event + // no buttons color + mMidiShiftState = data.status == 9 ? true : data.status == 8 ? false : mMidiShiftState; + } + else { + int8_t buttonIndex = static_cast(data.data1 - mButtonGroup * 8); + if (buttonIndex >= 0 && buttonIndex < 8) { + // pad button event + if (mButtonStates.at(buttonIndex) <= BUTTON_ALLOCATED) { + // if button has not been polled before user input, set button to off first time + mButtonStates.at(buttonIndex) = BUTTON_OFF; + } + else if (!mMidiShiftState && mButtonStates.at(buttonIndex) < BUTTON_ON) { + mButtonStates.at(buttonIndex)++; + } + else if (mMidiShiftState && mButtonStates.at(buttonIndex) > BUTTON_OFF) { + mButtonStates.at(buttonIndex)--; + } + float outputValue = static_cast(mButtonStates.at(buttonIndex) - BUTTON_OFF) / 2.0f; + mNode->SetInput(buttonIndex, outputValue); + UpdateButton(buttonIndex, mButtonStates.at(buttonIndex)); } } - float outputValue = static_cast(mButtonStates.at(buttonIndex) - BUTTON_OFF) / 2.0f; - mNode->SetInput(buttonIndex, outputValue); - UpdateButton(buttonIndex, mButtonStates.at(buttonIndex)); + }else if (data.status == 8) { + if (data.data1 >= 64 && data.data1 <= 67) { + // navigation buttons up, down, left, right + UpdateCommonButton(data.data1, 0); + } + else if (data.data1 >= 68 && data.data1 <= 71) { + // navigation buttons volume, pan, send, device + UpdateCommonButton(data.data1, 0); + } + else if (data.data1 >= 81 && data.data1 <= 86) { + // navigation buttons stops all clips, clip stop, solo, rec arm, mute, select + UpdateCommonButton(data.data1, 0); + } + else if (data.data1 >= 91 && data.data1 <= 93) { + // navigation buttons stops all clips, clip stop, solo, rec arm, mute, select + } + else if (data.data1 == 98) { + // shift button event + mMidiShiftState = data.status == 9 ? true : data.status == 8 ? false : mMidiShiftState; + } + } + else if (data.status == 14) { + // pb (fade wheel) + } } } From 6ccedb9122f85ff40461f66fd349d0db9fe41b98 Mon Sep 17 00:00:00 2001 From: lnd3 Date: Tue, 10 Sep 2024 23:12:32 +0200 Subject: [PATCH 11/42] Bug. --- packages/hid/include/hid/Midi.h | 2 +- .../nodegraph/include/nodegraph/NodeGraphOpInput.h | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/hid/include/hid/Midi.h b/packages/hid/include/hid/Midi.h index 46ca782c..bb11e29f 100644 --- a/packages/hid/include/hid/Midi.h +++ b/packages/hid/include/hid/Midi.h @@ -36,7 +36,7 @@ namespace l::hid::midi { return mInDevice >= 0; } bool HasMidiOut() { - return mInDevice >= 0; + return mOutDevice >= 0; } uint32_t GetMidiIn() { return static_cast(mInDevice); diff --git a/packages/nodegraph/include/nodegraph/NodeGraphOpInput.h b/packages/nodegraph/include/nodegraph/NodeGraphOpInput.h index 4d527b7d..5bc75db0 100644 --- a/packages/nodegraph/include/nodegraph/NodeGraphOpInput.h +++ b/packages/nodegraph/include/nodegraph/NodeGraphOpInput.h @@ -71,13 +71,13 @@ namespace l::nodegraph { { mChannel.resize(1); + SetDevice(0); + mMidiManager->RegisterCallback([&](l::hid::midi::MidiData data) { if (mMidiDeviceInId >= 0 && data.deviceIn == static_cast(mMidiDeviceInId)) { MidiEvent(data); } }); - - SetDevice(0); } std::string defaultOutStrings[5] = { "Freq", "Velocity", "Note On Id", "Note Off Id", "Device Id"}; @@ -139,13 +139,13 @@ namespace l::nodegraph { NodeGraphOp(node, 0, 9, 9), mMidiManager(midiManager) { + SetDevice(0); + mMidiManager->RegisterCallback([&](l::hid::midi::MidiData data) { if (mMidiDeviceInId >= 0 && data.deviceIn == static_cast(mMidiDeviceInId)) { MidiEvent(data); } }); - - SetDevice(0); } std::string defaultOutStrings[9] = { "Knob 1", "Knob 2", "Knob 3", "Knob 4", "Knob 5", "Knob 6", "Knob 7", "Knob 8", "Device Id"}; @@ -232,6 +232,8 @@ namespace l::nodegraph { } defaultOutStrings[8] = "Device Id"; + SetDevice(0); + if (mMidiManager) { mCallbackId = mMidiManager->RegisterCallback([&](l::hid::midi::MidiData data) { if (mMidiDeviceInId >= 0 && data.deviceIn == static_cast(mMidiDeviceInId)) { @@ -244,8 +246,6 @@ namespace l::nodegraph { UpdateButton(i, BUTTON_ALLOCATED); } } - - SetDevice(0); } std::string defaultOutStrings[9]; @@ -280,7 +280,7 @@ namespace l::nodegraph { if (mMidiManager) { int8_t buttonColor = remapToButtonStatesToColor.at(buttonState); auto deviceInfo = mMidiManager->GetDeviceInfo(mMidiDeviceInId); - if (deviceInfo) { + if (deviceInfo && deviceInfo->HasMidiOut()) { mMidiManager->SendToDevice(deviceInfo->GetMidiOut(), 0x90, 0, mButtonGroup * 8 + buttonId, buttonColor); } mButtonStates.at(buttonId) = buttonState; From 4305a254d5f6cb3467551368ec3c7639e29d43d0 Mon Sep 17 00:00:00 2001 From: lnd3 Date: Wed, 11 Sep 2024 14:22:54 +0200 Subject: [PATCH 12/42] Structure node graph files. Add generalized signal generators base. --- packages/math/include/math/MathFunc.h | 2 +- .../include/nodegraph/NodeGraphSchema.h | 30 ++- .../{NodeGraph.h => core/NodeGraphBase.h} | 175 +------------ .../include/nodegraph/core/NodeGraphGroup.h | 91 +++++++ .../include/nodegraph/core/NodeGraphInput.h | 65 +++++ .../include/nodegraph/core/NodeGraphOutput.h | 38 +++ .../{ => operations}/NodeGraphOpEffect.h | 2 +- .../{ => operations}/NodeGraphOpFilter.h | 33 ++- .../{ => operations}/NodeGraphOpInput.h | 2 +- .../{ => operations}/NodeGraphOpLogic.h | 2 +- .../{ => operations}/NodeGraphOpNumeric.h | 2 +- .../{ => operations}/NodeGraphOpOutput.h | 2 +- .../NodeGraphOpSignal.h} | 163 ++++++------ .../nodegraph/operations/NodeGraphOpSource.h | 85 +++++++ .../source/common/NodeGraphSchema.cpp | 37 +-- .../{NodeGraph.cpp => core/NodeGraphBase.cpp} | 199 +-------------- .../source/common/core/NodeGraphGroup.cpp | 135 ++++++++++ .../source/common/core/NodeGraphInput.cpp | 71 ++++++ .../source/common/core/NodeGraphOutput.cpp | 42 ++++ .../{ => operations}/NodeGraphOpEffect.cpp | 2 +- .../{ => operations}/NodeGraphOpFilter.cpp | 26 +- .../{ => operations}/NodeGraphOpInput.cpp | 2 +- .../{ => operations}/NodeGraphOpLogic.cpp | 2 +- .../{ => operations}/NodeGraphOpNumeric.cpp | 2 +- .../{ => operations}/NodeGraphOpOutput.cpp | 2 +- .../NodeGraphOpSignal.cpp} | 236 ++++++++++++------ .../common/operations/NodeGraphOpSource.cpp | 69 +++++ .../tests/common/NodeGraphSchemaTest.cpp | 2 +- .../include/rendering/ui/UICreator.h | 2 +- .../include/rendering/ui/UINodeEditor.h | 1 - 30 files changed, 962 insertions(+), 560 deletions(-) rename packages/nodegraph/include/nodegraph/{NodeGraph.h => core/NodeGraphBase.h} (58%) create mode 100644 packages/nodegraph/include/nodegraph/core/NodeGraphGroup.h create mode 100644 packages/nodegraph/include/nodegraph/core/NodeGraphInput.h create mode 100644 packages/nodegraph/include/nodegraph/core/NodeGraphOutput.h rename packages/nodegraph/include/nodegraph/{ => operations}/NodeGraphOpEffect.h (99%) rename packages/nodegraph/include/nodegraph/{ => operations}/NodeGraphOpFilter.h (56%) rename packages/nodegraph/include/nodegraph/{ => operations}/NodeGraphOpInput.h (99%) rename packages/nodegraph/include/nodegraph/{ => operations}/NodeGraphOpLogic.h (98%) rename packages/nodegraph/include/nodegraph/{ => operations}/NodeGraphOpNumeric.h (99%) rename packages/nodegraph/include/nodegraph/{ => operations}/NodeGraphOpOutput.h (98%) rename packages/nodegraph/include/nodegraph/{NodeGraphOpSource.h => operations/NodeGraphOpSignal.h} (63%) create mode 100644 packages/nodegraph/include/nodegraph/operations/NodeGraphOpSource.h rename packages/nodegraph/source/common/{NodeGraph.cpp => core/NodeGraphBase.cpp} (65%) create mode 100644 packages/nodegraph/source/common/core/NodeGraphGroup.cpp create mode 100644 packages/nodegraph/source/common/core/NodeGraphInput.cpp create mode 100644 packages/nodegraph/source/common/core/NodeGraphOutput.cpp rename packages/nodegraph/source/common/{ => operations}/NodeGraphOpEffect.cpp (99%) rename packages/nodegraph/source/common/{ => operations}/NodeGraphOpFilter.cpp (52%) rename packages/nodegraph/source/common/{ => operations}/NodeGraphOpInput.cpp (99%) rename packages/nodegraph/source/common/{ => operations}/NodeGraphOpLogic.cpp (72%) rename packages/nodegraph/source/common/{ => operations}/NodeGraphOpNumeric.cpp (71%) rename packages/nodegraph/source/common/{ => operations}/NodeGraphOpOutput.cpp (98%) rename packages/nodegraph/source/common/{NodeGraphOpSource.cpp => operations/NodeGraphOpSignal.cpp} (65%) create mode 100644 packages/nodegraph/source/common/operations/NodeGraphOpSource.cpp diff --git a/packages/math/include/math/MathFunc.h b/packages/math/include/math/MathFunc.h index 5b74ad42..3b74c50f 100644 --- a/packages/math/include/math/MathFunc.h +++ b/packages/math/include/math/MathFunc.h @@ -129,7 +129,7 @@ namespace l::math::functions { T mod(T val, T mod) { if constexpr (std::is_floating_point_v) { if constexpr (sizeof(T) == 4) { - return modff(val, mod); + return fmodf(val, mod); } else if constexpr (sizeof(T) == 8) { return fmod(val, mod); diff --git a/packages/nodegraph/include/nodegraph/NodeGraphSchema.h b/packages/nodegraph/include/nodegraph/NodeGraphSchema.h index 62e70205..9e6e25be 100644 --- a/packages/nodegraph/include/nodegraph/NodeGraphSchema.h +++ b/packages/nodegraph/include/nodegraph/NodeGraphSchema.h @@ -2,14 +2,15 @@ #include "logging/LoggingAll.h" -#include "nodegraph/NodeGraph.h" -#include "nodegraph/NodeGraphOpEffect.h" -#include "nodegraph/NodeGraphOpFilter.h" -#include "nodegraph/NodeGraphOpInput.h" -#include "nodegraph/NodeGraphOpLogic.h" -#include "nodegraph/NodeGraphOpNumeric.h" -#include "nodegraph/NodeGraphOpOutput.h" -#include "nodegraph/NodeGraphOpSource.h" +#include "nodegraph/core/NodeGraphGroup.h" +#include "nodegraph/operations/NodeGraphOpEffect.h" +#include "nodegraph/operations/NodeGraphOpFilter.h" +#include "nodegraph/operations/NodeGraphOpInput.h" +#include "nodegraph/operations/NodeGraphOpLogic.h" +#include "nodegraph/operations/NodeGraphOpNumeric.h" +#include "nodegraph/operations/NodeGraphOpOutput.h" +#include "nodegraph/operations/NodeGraphOpSource.h" +#include "nodegraph/operations/NodeGraphOpSignal.h" #include "hid/Midi.h" @@ -45,11 +46,6 @@ namespace l::nodegraph { RegisterNodeType("Source", 2, "Value [0,100]"); RegisterNodeType("Source", 3, "Value [-inf,inf]"); RegisterNodeType("Source", 4, "Time"); - RegisterNodeType("Source", 5, "Sine"); - RegisterNodeType("Source", 6, "Sine FM 1"); - RegisterNodeType("Source", 7, "Sine FM 2"); - RegisterNodeType("Source", 8, "Sine FM 3"); - RegisterNodeType("Source", 9, "Saw"); RegisterNodeType("Numeric", 50, "Add"); RegisterNodeType("Numeric", 51, "Subtract"); RegisterNodeType("Numeric", 52, "Negate"); @@ -62,6 +58,7 @@ namespace l::nodegraph { RegisterNodeType("Logic", 101, "Or"); RegisterNodeType("Logic", 102, "Xor"); RegisterNodeType("Filter", 150, "Lowpass"); + RegisterNodeType("Filter", 151, "Highpass"); RegisterNodeType("Output", 200, "Debug"); RegisterNodeType("Output", 201, "Speaker"); RegisterNodeType("Output", 202, "Plot"); @@ -81,6 +78,13 @@ namespace l::nodegraph { RegisterNodeType("Input", 305, "Midi Button Group 3"); RegisterNodeType("Input", 306, "Midi Button Group 4"); RegisterNodeType("Input", 307, "Midi Button Group 5"); + RegisterNodeType("Signal", 350, "Sine"); + RegisterNodeType("Signal", 351, "Sine FM 1"); + RegisterNodeType("Signal", 352, "Sine FM 2"); + RegisterNodeType("Signal", 353, "Sine FM 3"); + RegisterNodeType("Signal", 354, "Saw"); + RegisterNodeType("Signal", 355, "Sine 2"); + } ~NodeGraphSchema() = default; diff --git a/packages/nodegraph/include/nodegraph/NodeGraph.h b/packages/nodegraph/include/nodegraph/core/NodeGraphBase.h similarity index 58% rename from packages/nodegraph/include/nodegraph/NodeGraph.h rename to packages/nodegraph/include/nodegraph/core/NodeGraphBase.h index 35a5b999..46564442 100644 --- a/packages/nodegraph/include/nodegraph/NodeGraph.h +++ b/packages/nodegraph/include/nodegraph/core/NodeGraphBase.h @@ -11,9 +11,13 @@ #include "math/MathConstants.h" +#include "nodegraph/core/NodeGraphInput.h" +#include "nodegraph/core/NodeGraphOutput.h" + namespace l::nodegraph { int32_t CreateUniqueId(); + bool IsValidInOutNum(int8_t inoutNum, size_t inoutSize); enum class DataType { FLOAT32, @@ -21,107 +25,8 @@ namespace l::nodegraph { BITFIELD32 }; - enum class InputType { - INPUT_EMPTY, - INPUT_NODE, - INPUT_CONSTANT, - INPUT_VALUE, - INPUT_ARRAY - }; - - enum class InputBound { - INPUT_DONTCHANGE, - INPUT_UNBOUNDED, - INPUT_0_TO_1, - INPUT_0_TO_2, - INPUT_NEG_1_POS_1, - INPUT_0_100, - INPUT_CUSTOM, - }; - - enum class OutputType { - Default, // node will be processed if it is connected to the groups output by some route - ExternalOutput, // node does not have meaningful output for other nodes but should still be processed (ex speaker output only has input) - ExternalVisualOutput, - }; - - bool IsValidInOutNum(int8_t inoutNum, size_t inoutSize); - - class NodeGraphOutput { - public: - NodeGraphOutput() = default; - - float mOutput = 0.0f; - std::unique_ptr> mOutputBuf = nullptr; - std::unique_ptr mName = nullptr; - bool mOutputPolled = false; - - float& GetOutput(int32_t size = 1) { - if (!mOutputBuf) { - if (size <= 1) { - mOutputPolled = true; - return mOutput; - } - else { - mOutputBuf = std::make_unique>(); - } - } - if (static_cast(mOutputBuf->size()) < size) { - mOutputBuf->resize(size); - } - mOutputPolled = true; - return *mOutputBuf->data(); - } - - int32_t GetOutputSize() { - if (!mOutputBuf) { - return 1; - } - else { - return static_cast(mOutputBuf->size()); - } - } - - bool IsOutputPolled() { - return mOutputPolled; - } - - void ResetOutputPollState() { - mOutputPolled = false; - } - }; - - class NodeGraphBase; class NodeGraphGroup; - union Input { - NodeGraphBase* mInputNode = nullptr; - float* mInputFloat; - float mInputFloatConstant; - int32_t* mInputInt; - int32_t mInputIntConstant; - }; - - struct NodeGraphInput { - Input mInput; - InputType mInputType = InputType::INPUT_EMPTY; - - float mBoundMin = -l::math::constants::FLTMAX; - float mBoundMax = l::math::constants::FLTMAX; - InputBound mInputBound = InputBound::INPUT_UNBOUNDED; - - int8_t mInputFromOutputChannel = 0; - std::unique_ptr mName; - - // hack to get input buffers working - std::unique_ptr> mInputBuf = nullptr; - - void Reset(); - bool HasInputNode(); - float Get(); - float& Get(int32_t numSamples); - }; - class NodeGraphBase { public: NodeGraphBase(OutputType outputType) : mId(CreateUniqueId()), mOutputType(outputType) { @@ -232,16 +137,6 @@ namespace l::nodegraph { int8_t mNumConstants = 0; }; - class GraphDataCopy : public NodeGraphOp { - public: - GraphDataCopy(NodeGraphBase* node) : - NodeGraphOp(node, 0) - {} - virtual ~GraphDataCopy() = default; - - void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; - }; - template class NodeGraph : public NodeGraphBase { public: @@ -332,67 +227,5 @@ namespace l::nodegraph { T mOperation; }; - class NodeGraphGroup { - public: - NodeGraphGroup() : - mInputNode(OutputType::Default), - mOutputNode(OutputType::Default) - { - SetNumInputs(1); - SetNumOutputs(1); - mOutputNodes.push_back(&mOutputNode); - } - ~NodeGraphGroup() { - LOG(LogInfo) << "Node group destroyed"; - } - - void SetNumInputs(int8_t numInputs); - void SetNumOutputs(int8_t outputCount); - void SetInput(int8_t inputChannel, NodeGraphBase& source, int8_t sourceOutputChannel); - void SetInput(int8_t inputChannel, NodeGraphGroup& source, int8_t sourceOutputChannel); - void SetInput(int8_t inputChannel, float constant); - void SetInput(int8_t inputChannel, float* floatPtr); - - void SetOutput(int8_t outputChannel, NodeGraphBase& source, int8_t sourceOutputChannel); - void SetOutput(int8_t outputChannel, NodeGraphGroup& source, int8_t sourceOutputChannel); - - float GetOutput(int8_t outputChannel); - NodeGraphBase& GetInputNode(); - NodeGraphBase& GetOutputNode(); - - bool ContainsNode(int32_t id); - NodeGraphBase* GetNode(int32_t id); - - template>> - NodeGraph* GetTypedNode(int32_t id) { - auto p = GetNode(id); - return reinterpret_cast*>(p); - } - - bool RemoveNode(int32_t id); - - template>, class... Params> - l::nodegraph::NodeGraphBase* NewNode(OutputType nodeType, Params&&... params) { - mNodes.push_back(std::make_unique>(nodeType, std::forward(params)...)); - auto nodePtr = mNodes.back().get(); - if (nodeType == OutputType::ExternalOutput || nodeType == OutputType::ExternalVisualOutput) { - mOutputNodes.push_back(nodePtr); - } - return nodePtr; - } - - void ClearProcessFlags(); - void ProcessSubGraph(int32_t numSamples, bool recomputeSubGraphCache = true); - void Tick(int32_t tickCount, float elapsed); - protected: - NodeGraph mInputNode; - NodeGraph mOutputNode; - - std::vector> mNodes; - std::vector mOutputNodes; - - int32_t mLastTickCount = 0; - }; - } diff --git a/packages/nodegraph/include/nodegraph/core/NodeGraphGroup.h b/packages/nodegraph/include/nodegraph/core/NodeGraphGroup.h new file mode 100644 index 00000000..51d5955b --- /dev/null +++ b/packages/nodegraph/include/nodegraph/core/NodeGraphGroup.h @@ -0,0 +1,91 @@ +#pragma once + +#include "logging/LoggingAll.h" + +#include +#include +#include +#include +#include +#include + +#include "math/MathConstants.h" + +#include "nodegraph/core/NodeGraphBase.h" + +namespace l::nodegraph { + + class GraphDataCopy : public NodeGraphOp { + public: + GraphDataCopy(NodeGraphBase* node) : + NodeGraphOp(node, 0) + {} + virtual ~GraphDataCopy() = default; + + void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; + }; + + class NodeGraphGroup { + public: + NodeGraphGroup() : + mInputNode(OutputType::Default), + mOutputNode(OutputType::Default) + { + SetNumInputs(1); + SetNumOutputs(1); + mOutputNodes.push_back(&mOutputNode); + } + ~NodeGraphGroup() { + LOG(LogInfo) << "Node group destroyed"; + } + + void SetNumInputs(int8_t numInputs); + void SetNumOutputs(int8_t outputCount); + void SetInput(int8_t inputChannel, NodeGraphBase& source, int8_t sourceOutputChannel); + void SetInput(int8_t inputChannel, NodeGraphGroup& source, int8_t sourceOutputChannel); + void SetInput(int8_t inputChannel, float constant); + void SetInput(int8_t inputChannel, float* floatPtr); + + void SetOutput(int8_t outputChannel, NodeGraphBase& source, int8_t sourceOutputChannel); + void SetOutput(int8_t outputChannel, NodeGraphGroup& source, int8_t sourceOutputChannel); + + float GetOutput(int8_t outputChannel); + NodeGraphBase& GetInputNode(); + NodeGraphBase& GetOutputNode(); + + bool ContainsNode(int32_t id); + NodeGraphBase* GetNode(int32_t id); + + template>> + NodeGraph* GetTypedNode(int32_t id) { + auto p = GetNode(id); + return reinterpret_cast*>(p); + } + + bool RemoveNode(int32_t id); + + template>, class... Params> + l::nodegraph::NodeGraphBase* NewNode(OutputType nodeType, Params&&... params) { + mNodes.push_back(std::make_unique>(nodeType, std::forward(params)...)); + auto nodePtr = mNodes.back().get(); + if (nodeType == OutputType::ExternalOutput || nodeType == OutputType::ExternalVisualOutput) { + mOutputNodes.push_back(nodePtr); + } + return nodePtr; + } + + void ClearProcessFlags(); + void ProcessSubGraph(int32_t numSamples, bool recomputeSubGraphCache = true); + void Tick(int32_t tickCount, float elapsed); + protected: + NodeGraph mInputNode; + NodeGraph mOutputNode; + + std::vector> mNodes; + std::vector mOutputNodes; + + int32_t mLastTickCount = 0; + }; + +} + diff --git a/packages/nodegraph/include/nodegraph/core/NodeGraphInput.h b/packages/nodegraph/include/nodegraph/core/NodeGraphInput.h new file mode 100644 index 00000000..313185f3 --- /dev/null +++ b/packages/nodegraph/include/nodegraph/core/NodeGraphInput.h @@ -0,0 +1,65 @@ +#pragma once + +#include "logging/LoggingAll.h" + +#include +#include +#include +#include +#include +#include + +#include "math/MathConstants.h" + +namespace l::nodegraph { + + enum class InputType { + INPUT_EMPTY, + INPUT_NODE, + INPUT_CONSTANT, + INPUT_VALUE, + INPUT_ARRAY + }; + + enum class InputBound { + INPUT_DONTCHANGE, + INPUT_UNBOUNDED, + INPUT_0_TO_1, + INPUT_0_TO_2, + INPUT_NEG_1_POS_1, + INPUT_0_100, + INPUT_CUSTOM, + }; + + class NodeGraphBase; + + union Input { + NodeGraphBase* mInputNode = nullptr; + float* mInputFloat; + float mInputFloatConstant; + int32_t* mInputInt; + int32_t mInputIntConstant; + }; + + struct NodeGraphInput { + Input mInput; + InputType mInputType = InputType::INPUT_EMPTY; + + float mBoundMin = -l::math::constants::FLTMAX; + float mBoundMax = l::math::constants::FLTMAX; + InputBound mInputBound = InputBound::INPUT_UNBOUNDED; + + int8_t mInputFromOutputChannel = 0; + std::unique_ptr mName; + + // hack to get input buffers working + std::unique_ptr> mInputBuf = nullptr; + + void Reset(); + bool HasInputNode(); + float Get(); + float& Get(int32_t numSamples); + }; + +} + diff --git a/packages/nodegraph/include/nodegraph/core/NodeGraphOutput.h b/packages/nodegraph/include/nodegraph/core/NodeGraphOutput.h new file mode 100644 index 00000000..9e524481 --- /dev/null +++ b/packages/nodegraph/include/nodegraph/core/NodeGraphOutput.h @@ -0,0 +1,38 @@ +#pragma once + +#include "logging/LoggingAll.h" + +#include +#include +#include +#include +#include +#include + +#include "math/MathConstants.h" + +namespace l::nodegraph { + + enum class OutputType { + Default, // node will be processed if it is connected to the groups output by some route + ExternalOutput, // node does not have meaningful output for other nodes but should still be processed (ex speaker output only has input) + ExternalVisualOutput, + }; + + class NodeGraphOutput { + public: + NodeGraphOutput() = default; + + float mOutput = 0.0f; + std::unique_ptr> mOutputBuf = nullptr; + std::unique_ptr mName = nullptr; + bool mOutputPolled = false; + + float& GetOutput(int32_t size = 1); + int32_t GetOutputSize(); + bool IsOutputPolled(); + void ResetOutputPollState(); + }; + +} + diff --git a/packages/nodegraph/include/nodegraph/NodeGraphOpEffect.h b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpEffect.h similarity index 99% rename from packages/nodegraph/include/nodegraph/NodeGraphOpEffect.h rename to packages/nodegraph/include/nodegraph/operations/NodeGraphOpEffect.h index 6ad33142..d2f998f5 100644 --- a/packages/nodegraph/include/nodegraph/NodeGraphOpEffect.h +++ b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpEffect.h @@ -1,5 +1,5 @@ #pragma once -#include "nodegraph/NodeGraph.h" +#include "nodegraph/core/NodeGraphBase.h" #include "logging/LoggingAll.h" diff --git a/packages/nodegraph/include/nodegraph/NodeGraphOpFilter.h b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpFilter.h similarity index 56% rename from packages/nodegraph/include/nodegraph/NodeGraphOpFilter.h rename to packages/nodegraph/include/nodegraph/operations/NodeGraphOpFilter.h index 8efca744..e7009ef5 100644 --- a/packages/nodegraph/include/nodegraph/NodeGraphOpFilter.h +++ b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpFilter.h @@ -1,5 +1,5 @@ #pragma once -#include "nodegraph/NodeGraph.h" +#include "nodegraph/core/NodeGraphBase.h" #include "logging/LoggingAll.h" @@ -55,5 +55,36 @@ namespace l::nodegraph { float mState1 = 0.0f; }; + /*********************************************************************/ + class GraphFilterHighpass : public NodeGraphOp { + public: + std::string defaultInStrings[3] = { "Data", "Cutoff", "Resonance" }; + std::string defaultOutStrings[1] = { "Out" }; + + GraphFilterHighpass(NodeGraphBase* node) : + NodeGraphOp(node, 3, 1) + {} + + virtual ~GraphFilterHighpass() = default; + virtual void Reset() override; + virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; + virtual bool IsDataVisible(int8_t) override { return true; } + virtual bool IsDataEditable(int8_t channel) override { return channel > 0 ? 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 "Highpass"; + } + protected: + float mState0 = 0.0f; + float mState1 = 0.0f; + }; + } diff --git a/packages/nodegraph/include/nodegraph/NodeGraphOpInput.h b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpInput.h similarity index 99% rename from packages/nodegraph/include/nodegraph/NodeGraphOpInput.h rename to packages/nodegraph/include/nodegraph/operations/NodeGraphOpInput.h index 5bc75db0..dd497a9d 100644 --- a/packages/nodegraph/include/nodegraph/NodeGraphOpInput.h +++ b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpInput.h @@ -1,5 +1,5 @@ #pragma once -#include "nodegraph/NodeGraph.h" +#include "nodegraph/core/NodeGraphBase.h" #include "logging/LoggingAll.h" diff --git a/packages/nodegraph/include/nodegraph/NodeGraphOpLogic.h b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpLogic.h similarity index 98% rename from packages/nodegraph/include/nodegraph/NodeGraphOpLogic.h rename to packages/nodegraph/include/nodegraph/operations/NodeGraphOpLogic.h index abba62a9..cee4967b 100644 --- a/packages/nodegraph/include/nodegraph/NodeGraphOpLogic.h +++ b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpLogic.h @@ -1,5 +1,5 @@ #pragma once -#include "nodegraph/NodeGraph.h" +#include "nodegraph/core/NodeGraphBase.h" #include "logging/LoggingAll.h" diff --git a/packages/nodegraph/include/nodegraph/NodeGraphOpNumeric.h b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpNumeric.h similarity index 99% rename from packages/nodegraph/include/nodegraph/NodeGraphOpNumeric.h rename to packages/nodegraph/include/nodegraph/operations/NodeGraphOpNumeric.h index 04edf45b..67c63855 100644 --- a/packages/nodegraph/include/nodegraph/NodeGraphOpNumeric.h +++ b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpNumeric.h @@ -1,5 +1,5 @@ #pragma once -#include "nodegraph/NodeGraph.h" +#include "nodegraph/core/NodeGraphBase.h" #include "logging/LoggingAll.h" diff --git a/packages/nodegraph/include/nodegraph/NodeGraphOpOutput.h b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpOutput.h similarity index 98% rename from packages/nodegraph/include/nodegraph/NodeGraphOpOutput.h rename to packages/nodegraph/include/nodegraph/operations/NodeGraphOpOutput.h index 8557b6a8..a37229c2 100644 --- a/packages/nodegraph/include/nodegraph/NodeGraphOpOutput.h +++ b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpOutput.h @@ -1,5 +1,5 @@ #pragma once -#include "nodegraph/NodeGraph.h" +#include "nodegraph/core/NodeGraphBase.h" #include "logging/LoggingAll.h" diff --git a/packages/nodegraph/include/nodegraph/NodeGraphOpSource.h b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpSignal.h similarity index 63% rename from packages/nodegraph/include/nodegraph/NodeGraphOpSource.h rename to packages/nodegraph/include/nodegraph/operations/NodeGraphOpSignal.h index 86571c87..87f4bd45 100644 --- a/packages/nodegraph/include/nodegraph/NodeGraphOpSource.h +++ b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpSignal.h @@ -1,5 +1,5 @@ #pragma once -#include "nodegraph/NodeGraph.h" +#include "nodegraph/core/NodeGraphBase.h" #include "logging/LoggingAll.h" @@ -22,78 +22,99 @@ namespace l::nodegraph { - /* Mathematical operations */ - - /*********************************************************************/ - class GraphSourceConstants : public NodeGraphOp { + class GraphSignalBase : public NodeGraphOp { public: - GraphSourceConstants(NodeGraphBase* node, int32_t mode) : - NodeGraphOp(node, 0, 4, 4), - mMode(mode) + + static const int8_t mNumDefaultInputs = 6; + static const int8_t mNumDefaultOutputs = 1; + + GraphSignalBase(NodeGraphBase* node, std::string_view name, int32_t numInputs = 0, int32_t numOutputs = 0, int32_t numConstants = 0) : + NodeGraphOp(node, mNumDefaultInputs + numInputs, mNumDefaultOutputs + numOutputs, numConstants), + mName(name) {} - virtual ~GraphSourceConstants() = default; - virtual void Reset(); - virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; - virtual void Tick(int32_t, float) override; - virtual std::string_view GetName() override { - switch (mMode) { - case 0: - return "Constant [0,1]"; - case 1: - return "Constant [-1,1]"; - case 2: - return "Constant [0,100]"; - case 3: - return "Constant [-inf,inf]"; - }; + std::string defaultInStrings[mNumDefaultInputs] = { "Reset", "Freq", "Volume", "Smooth", "Cutoff", "Resonance"}; + std::string defaultOutStrings[mNumDefaultOutputs] = { "Out" }; + + virtual ~GraphSignalBase() = default; + virtual void Reset() override final; + virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override final; + virtual bool IsDataVisible(int8_t) override { return true; } + virtual bool IsDataEditable(int8_t) override { return true; } + virtual std::string_view GetInputName(int8_t inputChannel) override final { + if (inputChannel >= mNumDefaultInputs) return GetInputNameExtra(inputChannel - mNumDefaultInputs); + if (inputChannel >= 0) return defaultInStrings[static_cast(inputChannel)]; return ""; } - virtual bool IsDataVisible(int8_t) override {return true;} - virtual bool IsDataEditable(int8_t) override {return true;} + virtual std::string_view GetOutputName(int8_t outputChannel) override final { + if (outputChannel >= mNumDefaultOutputs) return GetOutputNameExtra(outputChannel - mNumDefaultOutputs); + return defaultOutStrings[outputChannel]; + } + virtual std::string_view GetName() override { + return mName; + } + + virtual std::string_view GetInputNameExtra(int8_t) { return ""; }; + virtual std::string_view GetOutputNameExtra(int8_t) { return ""; }; + virtual void ResetSignal() {}; + virtual void UpdateSignal(std::vector&, std::vector&) {}; + virtual float GenerateSignal(float deltaPhase) = 0; protected: - int32_t mMode; - float mMax = 1.0f; - float mMin = 0.0f; + std::string mName; + + float mReset = 0.0f; + float mFreq = 0.0f; + float mVolume = 0.0f; + float mSmooth = 0.5f; + float mSignal = 0.0f; + float mWave = 0.0f; + float mDeltaPhase = 0.0f; + float mDeltaTime = 0.0f; + float mVolumeTarget = 0.0f; + float mSamplesUntilUpdate = 0.0f; + float mUpdateSamples = 256.0f; + + // high pass + float mHPCutoff = 0.5f; + float mHPResonance = 0.0001f; + float mHPState0 = 0.0f; + float mHPState1 = 0.0f; }; /*********************************************************************/ - class GraphSourceTime : public NodeGraphOp { + class GraphSignalSine2 : public GraphSignalBase { public: - GraphSourceTime(NodeGraphBase* node) : - NodeGraphOp(node, 0, 2, 0) + GraphSignalSine2(NodeGraphBase* node) : + GraphSignalBase(node, "Sine 2", 2) {} + std::string extraString[2] = { "Fmod", "Phase" }; - std::string defaultOutStrings[2] = { "Audio Time", "Frame Time"}; - - virtual ~GraphSourceTime() = default; - virtual void Reset() override; - virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; - virtual void Tick(int32_t, float) override; - virtual std::string_view GetOutputName(int8_t outputChannel) override { - return defaultOutStrings[outputChannel]; - } - virtual std::string_view GetName() override { - return "Time"; + virtual ~GraphSignalSine2() = default; + virtual std::string_view GetInputNameExtra(int8_t extraInputChannel) override { + if(extraInputChannel < 2) return extraString[static_cast(extraInputChannel)]; + return ""; } - virtual bool IsDataVisible(int8_t) override { return true; } - virtual bool IsDataEditable(int8_t) override { return true; } + void ResetSignal() override; + void UpdateSignal(std::vector& inputs, std::vector& outputs) override; + float GenerateSignal(float deltaPhase) override; protected: - float mAudioTime = 0.0f; - float mFrameTime = 0.0f; + float mFmod = 0.0f; + float mPmod = 0.0f; + float mPhase = 0.0f; + float mPhaseFmod = 0.0f; }; /*********************************************************************/ - class GraphSourceSine : public NodeGraphOp { + class GraphSignalSine : public NodeGraphOp { public: - GraphSourceSine(NodeGraphBase* node) : - NodeGraphOp(node, 5, 1) + GraphSignalSine(NodeGraphBase* node) : + NodeGraphOp(node, 6, 1) {} - std::string defaultInStrings[5] = { "Freq", "Volume", "Fmod", "Phase", "Reset"}; + std::string defaultInStrings[6] = { "Freq", "Volume", "Fmod", "Phase", "Smooth", "Reset"}; std::string defaultOutStrings[1] = { "Out"}; - virtual ~GraphSourceSine() = default; + virtual ~GraphSignalSine() = default; virtual void Reset() override; virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; virtual bool IsDataVisible(int8_t) override { return true; } @@ -125,16 +146,16 @@ namespace l::nodegraph { }; /*********************************************************************/ - class GraphSourceSineFM : public NodeGraphOp { + class GraphSignalSineFM : public NodeGraphOp { public: - GraphSourceSineFM(NodeGraphBase* node) : - NodeGraphOp(node, 8, 1) + GraphSignalSineFM(NodeGraphBase* node) : + NodeGraphOp(node, 9, 1) {} - std::string defaultInStrings[8] = { "Freq", "Volume", "Fmod", "FmodFreq", "FmodVol", "FmodOfs", "FmodGain", "Reset"}; + std::string defaultInStrings[9] = { "Freq", "Volume", "Fmod", "FmodFreq", "FmodVol", "FmodOfs", "FmodGain", "Smooth", "Reset"}; std::string defaultOutStrings[1] = { "Out" }; - virtual ~GraphSourceSineFM() = default; + virtual ~GraphSignalSineFM() = default; virtual void Reset() override; virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; virtual bool IsDataVisible(int8_t) override { return true; } @@ -171,16 +192,16 @@ namespace l::nodegraph { }; /*********************************************************************/ - class GraphSourceSineFM2 : public NodeGraphOp { + class GraphSignalSineFM2 : public NodeGraphOp { public: - GraphSourceSineFM2(NodeGraphBase* node) : - NodeGraphOp(node, 5, 1) + GraphSignalSineFM2(NodeGraphBase* node) : + NodeGraphOp(node, 6, 1) {} - std::string defaultInStrings[5] = { "Freq", "Volume", "FmodVol", "FmodFreq", "Reset" }; + std::string defaultInStrings[6] = { "Freq", "Volume", "FmodVol", "FmodFreq", "Smooth", "Reset" }; std::string defaultOutStrings[1] = { "Out" }; - virtual ~GraphSourceSineFM2() = default; + virtual ~GraphSignalSineFM2() = default; virtual void Reset() override; virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; virtual bool IsDataVisible(int8_t) override { return true; } @@ -212,16 +233,16 @@ namespace l::nodegraph { }; /*********************************************************************/ - class GraphSourceSineFM3 : public NodeGraphOp { + class GraphSignalSineFM3 : public NodeGraphOp { public: - GraphSourceSineFM3(NodeGraphBase* node) : - NodeGraphOp(node, 4, 1) + GraphSignalSineFM3(NodeGraphBase* node) : + NodeGraphOp(node, 5, 1) {} - std::string defaultInStrings[4] = { "Freq", "Volume", "Fmod", "Reset" }; + std::string defaultInStrings[5] = { "Freq", "Volume", "Fmod", "Smooth", "Reset" }; std::string defaultOutStrings[1] = { "Out" }; - virtual ~GraphSourceSineFM3() = default; + virtual ~GraphSignalSineFM3() = default; virtual void Reset() override; virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; virtual bool IsDataVisible(int8_t) override { return true; } @@ -250,16 +271,16 @@ namespace l::nodegraph { }; /*********************************************************************/ - class GraphSourceSaw : public NodeGraphOp { + class GraphSignalSaw : public NodeGraphOp { public: - GraphSourceSaw(NodeGraphBase* node) : - NodeGraphOp(node, 5, 1) + GraphSignalSaw(NodeGraphBase* node) : + NodeGraphOp(node, 6, 1) {} - std::string defaultInStrings[5] = { "Freq", "Volume", "Fmod", "Phase", "Reset" }; + std::string defaultInStrings[6] = { "Freq", "Volume", "Fmod", "Phase", "Smooth", "Reset" }; std::string defaultOutStrings[1] = { "Out" }; - virtual ~GraphSourceSaw() = default; + virtual ~GraphSignalSaw() = default; virtual void Reset() override; virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; virtual bool IsDataVisible(int8_t) override { return true; } diff --git a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpSource.h b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpSource.h new file mode 100644 index 00000000..381b8ae8 --- /dev/null +++ b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpSource.h @@ -0,0 +1,85 @@ +#pragma once +#include "nodegraph/core/NodeGraphBase.h" + +#include "logging/LoggingAll.h" + +#include "hid/KeyboardPiano.h" +#include "hid/Midi.h" + +#include "audio/PortAudio.h" +#include "audio/AudioUtils.h" + +#include "math/MathFunc.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace l::nodegraph { + + /*********************************************************************/ + class GraphSourceConstants : public NodeGraphOp { + public: + GraphSourceConstants(NodeGraphBase* node, int32_t mode) : + NodeGraphOp(node, 0, 4, 4), + mMode(mode) + {} + + virtual ~GraphSourceConstants() = default; + virtual void Reset(); + virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; + virtual void Tick(int32_t, float) override; + virtual std::string_view GetName() override { + switch (mMode) { + case 0: + return "Constant [0,1]"; + case 1: + return "Constant [-1,1]"; + case 2: + return "Constant [0,100]"; + case 3: + return "Constant [-inf,inf]"; + }; + return ""; + } + virtual bool IsDataVisible(int8_t) override {return true;} + virtual bool IsDataEditable(int8_t) override {return true;} + protected: + int32_t mMode; + float mMax = 1.0f; + float mMin = 0.0f; + }; + + /*********************************************************************/ + class GraphSourceTime : public NodeGraphOp { + public: + GraphSourceTime(NodeGraphBase* node) : + NodeGraphOp(node, 0, 2, 0) + {} + + std::string defaultOutStrings[2] = { "Audio Time", "Frame Time"}; + + virtual ~GraphSourceTime() = default; + virtual void Reset() override; + virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; + virtual void Tick(int32_t, float) override; + virtual std::string_view GetOutputName(int8_t outputChannel) override { + return defaultOutStrings[outputChannel]; + } + virtual std::string_view GetName() override { + return "Time"; + } + virtual bool IsDataVisible(int8_t) override { return true; } + virtual bool IsDataEditable(int8_t) override { return true; } + protected: + float mAudioTime = 0.0f; + float mFrameTime = 0.0f; + }; + +} + diff --git a/packages/nodegraph/source/common/NodeGraphSchema.cpp b/packages/nodegraph/source/common/NodeGraphSchema.cpp index 3a08019f..527bf93a 100644 --- a/packages/nodegraph/source/common/NodeGraphSchema.cpp +++ b/packages/nodegraph/source/common/NodeGraphSchema.cpp @@ -40,21 +40,6 @@ namespace l::nodegraph { case 4: node = mMainNodeGraph.NewNode(OutputType::Default); break; - case 5: - node = mMainNodeGraph.NewNode(OutputType::Default); - break; - case 6: - node = mMainNodeGraph.NewNode(OutputType::Default); - break; - case 7: - node = mMainNodeGraph.NewNode(OutputType::Default); - break; - case 8: - node = mMainNodeGraph.NewNode(OutputType::Default); - break; - case 9: - node = mMainNodeGraph.NewNode(OutputType::Default); - break; case 50: node = mMainNodeGraph.NewNode(OutputType::Default); break; @@ -91,6 +76,9 @@ namespace l::nodegraph { case 150: node = mMainNodeGraph.NewNode(OutputType::Default); break; + case 151: + node = mMainNodeGraph.NewNode(OutputType::Default); + break; case 200: node = mMainNodeGraph.NewNode(OutputType::ExternalOutput); break; @@ -148,6 +136,25 @@ namespace l::nodegraph { case 307: node = mMainNodeGraph.NewNode(OutputType::Default, mMidiManager, 4); break; + case 350: + node = mMainNodeGraph.NewNode(OutputType::Default); + break; + case 351: + node = mMainNodeGraph.NewNode(OutputType::Default); + break; + case 352: + node = mMainNodeGraph.NewNode(OutputType::Default); + break; + case 353: + node = mMainNodeGraph.NewNode(OutputType::Default); + break; + case 354: + node = mMainNodeGraph.NewNode(OutputType::Default); + break; + case 355: + node = mMainNodeGraph.NewNode(OutputType::Default); + break; + default: ASSERT(typeId < 10000) << "Custom node id's begin at id 1000"; diff --git a/packages/nodegraph/source/common/NodeGraph.cpp b/packages/nodegraph/source/common/core/NodeGraphBase.cpp similarity index 65% rename from packages/nodegraph/source/common/NodeGraph.cpp rename to packages/nodegraph/source/common/core/NodeGraphBase.cpp index e6b27df3..ff495612 100644 --- a/packages/nodegraph/source/common/NodeGraph.cpp +++ b/packages/nodegraph/source/common/core/NodeGraphBase.cpp @@ -1,4 +1,5 @@ -#include "nodegraph/NodeGraph.h" +#include "nodegraph/core/NodeGraphBase.h" +#include "nodegraph/core/NodeGraphGroup.h" #include "logging/Log.h" @@ -6,15 +7,15 @@ namespace l::nodegraph { - bool IsValidInOutNum(int8_t inoutNum, size_t inoutSize) { - return inoutNum >= 0 && inoutSize < 256u && inoutNum < static_cast(inoutSize); - } - int32_t CreateUniqueId() { static int32_t id = 1; return id++; } + bool IsValidInOutNum(int8_t inoutNum, size_t inoutSize) { + return inoutNum >= 0 && inoutSize < 256u && inoutNum < static_cast(inoutSize); + } + void NodeGraphBase::SetNumInputs(int8_t numInputs) { mInputCount = numInputs; mInputs.resize(mInputCount + mConstantCount); @@ -173,6 +174,7 @@ namespace l::nodegraph { return false; } auto& input = mInputs.at(inputChannel); + constant = l::math::functions::clamp(constant, input.mBoundMin, input.mBoundMax); if (size <= 0) { input.mInput.mInputFloatConstant = constant; input.mInputType = InputType::INPUT_CONSTANT; @@ -358,191 +360,4 @@ namespace l::nodegraph { return ""; } - void GraphDataCopy::Process(int32_t, std::vector& inputs, std::vector& outputs) { - for (size_t i = 0; i < inputs.size() && i < outputs.size(); i++) { - outputs.at(i).mOutput = inputs.at(i).Get(); - } - } - - void NodeGraphInput::Reset() { - if (mInputType == InputType::INPUT_NODE || mInputType == InputType::INPUT_VALUE) { - mInput.mInputNode = nullptr; - mInputType = InputType::INPUT_EMPTY; - mInputFromOutputChannel = 0; - } - } - - bool NodeGraphInput::HasInputNode() { - if (mInputType == InputType::INPUT_NODE) { - return true; - } - return false; - } - - float NodeGraphInput::Get() { - float value = 0.0f; - switch (mInputType) { - case InputType::INPUT_NODE: - if (mInput.mInputNode != nullptr) { - value = mInput.mInputNode->GetOutput(mInputFromOutputChannel); - } - break; - case InputType::INPUT_CONSTANT: - value = mInput.mInputFloatConstant; - break; - case InputType::INPUT_ARRAY: - return *mInputBuf->data(); - case InputType::INPUT_VALUE: - value = *mInput.mInputFloat; - break; - case InputType::INPUT_EMPTY: - break; - } - return l::math::functions::clamp(value, mBoundMin, mBoundMax); - } - - float& NodeGraphInput::Get(int32_t size) { - switch (mInputType) { - case InputType::INPUT_NODE: - if (mInput.mInputNode != nullptr) { - return mInput.mInputNode->GetOutput(mInputFromOutputChannel, size); - } - break; - case InputType::INPUT_ARRAY: - if (!mInputBuf) { - mInputBuf = std::make_unique>(); - } - if (static_cast(mInputBuf->size()) < size) { - mInputBuf->resize(size); - } - return *mInputBuf->data(); - case InputType::INPUT_CONSTANT: - return mInput.mInputFloatConstant; - case InputType::INPUT_VALUE: - return *mInput.mInputFloat; - case InputType::INPUT_EMPTY: - break; - } - return mInput.mInputFloatConstant; - } - - void NodeGraphGroup::SetNumInputs(int8_t numInputs) { - mInputNode.SetNumInputs(numInputs); - mInputNode.SetNumOutputs(numInputs); - } - - void NodeGraphGroup::SetNumOutputs(int8_t outputCount) { - mOutputNode.SetNumInputs(outputCount); - mOutputNode.SetNumOutputs(outputCount); - } - - void NodeGraphGroup::SetInput(int8_t inputChannel, NodeGraphBase& source, int8_t sourceOutputChannel) { - mInputNode.SetInput(inputChannel, source, sourceOutputChannel); - } - - void NodeGraphGroup::SetInput(int8_t inputChannel, NodeGraphGroup& source, int8_t sourceOutputChannel) { - mInputNode.SetInput(inputChannel, source, sourceOutputChannel); - } - - void NodeGraphGroup::SetInput(int8_t inputChannel, float constant) { - mInputNode.SetInput(inputChannel, constant); - } - - void NodeGraphGroup::SetInput(int8_t inputChannel, float* floatPtr) { - mInputNode.SetInput(inputChannel, floatPtr); - } - - void NodeGraphGroup::SetOutput(int8_t outputChannel, NodeGraphBase& source, int8_t sourceOutputChannel) { - mOutputNode.SetInput(outputChannel, source, sourceOutputChannel); - mOutputNode.SetOutputName(outputChannel, source.GetOutputName(sourceOutputChannel)); - } - - void NodeGraphGroup::SetOutput(int8_t outputChannel, NodeGraphGroup& source, int8_t sourceOutputChannel) { - mOutputNode.SetInput(outputChannel, source, sourceOutputChannel); - mOutputNode.SetOutputName(outputChannel, source.GetOutputNode().GetOutputName(sourceOutputChannel)); - } - - float NodeGraphGroup::GetOutput(int8_t outputChannel) { - return mOutputNode.GetOutput(outputChannel); - } - - NodeGraphBase& NodeGraphGroup::GetInputNode() { - return mInputNode; - } - - NodeGraphBase& NodeGraphGroup::GetOutputNode() { - return mOutputNode; - } - - bool NodeGraphGroup::ContainsNode(int32_t id) { - auto it = std::find_if(mNodes.begin(), mNodes.end(), [&](const std::unique_ptr& node) { - if (node->GetId() == id) { - return true; - } - return false; - }); - if (it != mNodes.end()) { - return true; - } - return false; - } - - NodeGraphBase* NodeGraphGroup::GetNode(int32_t id) { - auto it = std::find_if(mNodes.begin(), mNodes.end(), [&](const std::unique_ptr& node) { - if (node->GetId() == id) { - return true; - } - return false; - }); - if (it != mNodes.end()) { - return it->get(); - } - return nullptr; - } - - bool NodeGraphGroup::RemoveNode(int32_t id) { - auto node = GetNode(id); - int32_t sourceCount = 0; - for (auto& it : mNodes) { - if (it->RemoveInput(node)) { - sourceCount++; - } - } - std::erase_if(mOutputNodes, [&](NodeGraphBase* nodePtr) { - if (nodePtr == node) { - return true; - } - return false; - }); - auto count = std::erase_if(mNodes, [&](const std::unique_ptr& node) { - if (node->GetId() == id) { - return true; - } - return false; - }); - return count > 0 ? true : false; - } - - void NodeGraphGroup::ClearProcessFlags() { - mOutputNode.ClearProcessFlags(); - } - - void NodeGraphGroup::ProcessSubGraph(int32_t numSamples, bool) { - for (auto& it : mOutputNodes) { - it->ClearProcessFlags(); - } - for (auto& it : mOutputNodes) { - it->ProcessSubGraph(numSamples, false); - } - } - - void NodeGraphGroup::Tick(int32_t tickCount, float elapsed) { - if (tickCount <= mLastTickCount) { - return; - } - for (auto& it : mNodes) { - it->Tick(tickCount, elapsed); - } - mLastTickCount = tickCount; - } } \ No newline at end of file diff --git a/packages/nodegraph/source/common/core/NodeGraphGroup.cpp b/packages/nodegraph/source/common/core/NodeGraphGroup.cpp new file mode 100644 index 00000000..7c6a3aab --- /dev/null +++ b/packages/nodegraph/source/common/core/NodeGraphGroup.cpp @@ -0,0 +1,135 @@ +#include "nodegraph/core/NodeGraphBase.h" +#include "nodegraph/core/NodeGraphGroup.h" + +#include "logging/Log.h" + +#include "math/MathFunc.h" + +namespace l::nodegraph { + + void GraphDataCopy::Process(int32_t, std::vector& inputs, std::vector& outputs) { + for (size_t i = 0; i < inputs.size() && i < outputs.size(); i++) { + outputs.at(i).mOutput = inputs.at(i).Get(); + } + } + + void NodeGraphGroup::SetNumInputs(int8_t numInputs) { + mInputNode.SetNumInputs(numInputs); + mInputNode.SetNumOutputs(numInputs); + } + + void NodeGraphGroup::SetNumOutputs(int8_t outputCount) { + mOutputNode.SetNumInputs(outputCount); + mOutputNode.SetNumOutputs(outputCount); + } + + void NodeGraphGroup::SetInput(int8_t inputChannel, NodeGraphBase& source, int8_t sourceOutputChannel) { + mInputNode.SetInput(inputChannel, source, sourceOutputChannel); + } + + void NodeGraphGroup::SetInput(int8_t inputChannel, NodeGraphGroup& source, int8_t sourceOutputChannel) { + mInputNode.SetInput(inputChannel, source, sourceOutputChannel); + } + + void NodeGraphGroup::SetInput(int8_t inputChannel, float constant) { + mInputNode.SetInput(inputChannel, constant); + } + + void NodeGraphGroup::SetInput(int8_t inputChannel, float* floatPtr) { + mInputNode.SetInput(inputChannel, floatPtr); + } + + void NodeGraphGroup::SetOutput(int8_t outputChannel, NodeGraphBase& source, int8_t sourceOutputChannel) { + mOutputNode.SetInput(outputChannel, source, sourceOutputChannel); + mOutputNode.SetOutputName(outputChannel, source.GetOutputName(sourceOutputChannel)); + } + + void NodeGraphGroup::SetOutput(int8_t outputChannel, NodeGraphGroup& source, int8_t sourceOutputChannel) { + mOutputNode.SetInput(outputChannel, source, sourceOutputChannel); + mOutputNode.SetOutputName(outputChannel, source.GetOutputNode().GetOutputName(sourceOutputChannel)); + } + + float NodeGraphGroup::GetOutput(int8_t outputChannel) { + return mOutputNode.GetOutput(outputChannel); + } + + NodeGraphBase& NodeGraphGroup::GetInputNode() { + return mInputNode; + } + + NodeGraphBase& NodeGraphGroup::GetOutputNode() { + return mOutputNode; + } + + bool NodeGraphGroup::ContainsNode(int32_t id) { + auto it = std::find_if(mNodes.begin(), mNodes.end(), [&](const std::unique_ptr& node) { + if (node->GetId() == id) { + return true; + } + return false; + }); + if (it != mNodes.end()) { + return true; + } + return false; + } + + NodeGraphBase* NodeGraphGroup::GetNode(int32_t id) { + auto it = std::find_if(mNodes.begin(), mNodes.end(), [&](const std::unique_ptr& node) { + if (node->GetId() == id) { + return true; + } + return false; + }); + if (it != mNodes.end()) { + return it->get(); + } + return nullptr; + } + + bool NodeGraphGroup::RemoveNode(int32_t id) { + auto node = GetNode(id); + int32_t sourceCount = 0; + for (auto& it : mNodes) { + if (it->RemoveInput(node)) { + sourceCount++; + } + } + std::erase_if(mOutputNodes, [&](NodeGraphBase* nodePtr) { + if (nodePtr == node) { + return true; + } + return false; + }); + auto count = std::erase_if(mNodes, [&](const std::unique_ptr& node) { + if (node->GetId() == id) { + return true; + } + return false; + }); + return count > 0 ? true : false; + } + + void NodeGraphGroup::ClearProcessFlags() { + mOutputNode.ClearProcessFlags(); + } + + void NodeGraphGroup::ProcessSubGraph(int32_t numSamples, bool) { + for (auto& it : mOutputNodes) { + it->ClearProcessFlags(); + } + for (auto& it : mOutputNodes) { + it->ProcessSubGraph(numSamples, false); + } + } + + void NodeGraphGroup::Tick(int32_t tickCount, float elapsed) { + if (tickCount <= mLastTickCount) { + return; + } + for (auto& it : mNodes) { + it->Tick(tickCount, elapsed); + } + mLastTickCount = tickCount; + } +} \ No newline at end of file diff --git a/packages/nodegraph/source/common/core/NodeGraphInput.cpp b/packages/nodegraph/source/common/core/NodeGraphInput.cpp new file mode 100644 index 00000000..51456423 --- /dev/null +++ b/packages/nodegraph/source/common/core/NodeGraphInput.cpp @@ -0,0 +1,71 @@ +#include "nodegraph/core/NodeGraphInput.h" +#include "nodegraph/core/NodeGraphBase.h" + +#include "logging/Log.h" + +#include "math/MathFunc.h" + +namespace l::nodegraph { + void NodeGraphInput::Reset() { + if (mInputType == InputType::INPUT_NODE || mInputType == InputType::INPUT_VALUE) { + mInput.mInputNode = nullptr; + mInputType = InputType::INPUT_EMPTY; + mInputFromOutputChannel = 0; + } + } + + bool NodeGraphInput::HasInputNode() { + if (mInputType == InputType::INPUT_NODE) { + return true; + } + return false; + } + + float NodeGraphInput::Get() { + float value = 0.0f; + switch (mInputType) { + case InputType::INPUT_NODE: + if (mInput.mInputNode != nullptr) { + value = mInput.mInputNode->GetOutput(mInputFromOutputChannel); + } + break; + case InputType::INPUT_CONSTANT: + value = mInput.mInputFloatConstant; + break; + case InputType::INPUT_ARRAY: + return *mInputBuf->data(); + case InputType::INPUT_VALUE: + value = *mInput.mInputFloat; + break; + case InputType::INPUT_EMPTY: + break; + } + return l::math::functions::clamp(value, mBoundMin, mBoundMax); + } + + float& NodeGraphInput::Get(int32_t size) { + switch (mInputType) { + case InputType::INPUT_NODE: + if (mInput.mInputNode != nullptr) { + return mInput.mInputNode->GetOutput(mInputFromOutputChannel, size); + } + break; + case InputType::INPUT_ARRAY: + if (!mInputBuf) { + mInputBuf = std::make_unique>(); + } + if (static_cast(mInputBuf->size()) < size) { + mInputBuf->resize(size); + } + return *mInputBuf->data(); + case InputType::INPUT_CONSTANT: + return mInput.mInputFloatConstant; + case InputType::INPUT_VALUE: + return *mInput.mInputFloat; + case InputType::INPUT_EMPTY: + break; + } + return mInput.mInputFloatConstant; + } + +} \ No newline at end of file diff --git a/packages/nodegraph/source/common/core/NodeGraphOutput.cpp b/packages/nodegraph/source/common/core/NodeGraphOutput.cpp new file mode 100644 index 00000000..1d1eff1c --- /dev/null +++ b/packages/nodegraph/source/common/core/NodeGraphOutput.cpp @@ -0,0 +1,42 @@ +#include "nodegraph/core/NodeGraphOutput.h" + +#include "logging/Log.h" + +#include "math/MathFunc.h" + +namespace l::nodegraph { + float& NodeGraphOutput::GetOutput(int32_t size) { + if (!mOutputBuf) { + if (size <= 1) { + mOutputPolled = true; + return mOutput; + } + else { + mOutputBuf = std::make_unique>(); + } + } + if (static_cast(mOutputBuf->size()) < size) { + mOutputBuf->resize(size); + } + mOutputPolled = true; + return *mOutputBuf->data(); + } + + int32_t NodeGraphOutput::GetOutputSize() { + if (!mOutputBuf) { + return 1; + } + else { + return static_cast(mOutputBuf->size()); + } + } + + bool NodeGraphOutput::IsOutputPolled() { + return mOutputPolled; + } + + void NodeGraphOutput::ResetOutputPollState() { + mOutputPolled = false; + } + +} \ No newline at end of file diff --git a/packages/nodegraph/source/common/NodeGraphOpEffect.cpp b/packages/nodegraph/source/common/operations/NodeGraphOpEffect.cpp similarity index 99% rename from packages/nodegraph/source/common/NodeGraphOpEffect.cpp rename to packages/nodegraph/source/common/operations/NodeGraphOpEffect.cpp index f35dd0b2..c497b369 100644 --- a/packages/nodegraph/source/common/NodeGraphOpEffect.cpp +++ b/packages/nodegraph/source/common/operations/NodeGraphOpEffect.cpp @@ -1,4 +1,4 @@ -#include "nodegraph/NodeGraphOpEffect.h" +#include "nodegraph/operations/NodeGraphOpEffect.h" #include "logging/Log.h" #include "audio/AudioUtils.h" diff --git a/packages/nodegraph/source/common/NodeGraphOpFilter.cpp b/packages/nodegraph/source/common/operations/NodeGraphOpFilter.cpp similarity index 52% rename from packages/nodegraph/source/common/NodeGraphOpFilter.cpp rename to packages/nodegraph/source/common/operations/NodeGraphOpFilter.cpp index 6c73d713..0bc5e164 100644 --- a/packages/nodegraph/source/common/NodeGraphOpFilter.cpp +++ b/packages/nodegraph/source/common/operations/NodeGraphOpFilter.cpp @@ -1,4 +1,4 @@ -#include "nodegraph/NodeGraphOpFilter.h" +#include "nodegraph/operations/NodeGraphOpFilter.h" #include "logging/Log.h" #include "audio/AudioUtils.h" @@ -35,4 +35,28 @@ namespace l::nodegraph { outputs.at(0).mOutput = -mState1; } + /*********************************************************************/ + void GraphFilterHighpass::Reset() { + mState0 = 0.0f; + mState1 = 0.0f; + mNode->SetInput(1, 0.99f); + mNode->SetInput(2, 0.01f); + mNode->SetInputBound(1, InputBound::INPUT_0_TO_1); + mNode->SetInputBound(2, InputBound::INPUT_0_TO_1); + } + + void GraphFilterHighpass::Process(int32_t, std::vector& inputs, std::vector& outputs) { + float inputValue = inputs.at(0).Get(); + float cutoff = inputs.at(1).Get(); + float resonance = 1.0f - inputs.at(2).Get(); + + cutoff *= cutoff; + float rc = 1.0f - resonance * cutoff; + + float v01 = mState0 - mState1; + mState0 += cutoff * (inputValue - mState0 + rc * v01); + mState1 += cutoff * v01; + + outputs.at(0).mOutput = inputValue - mState1; + } } diff --git a/packages/nodegraph/source/common/NodeGraphOpInput.cpp b/packages/nodegraph/source/common/operations/NodeGraphOpInput.cpp similarity index 99% rename from packages/nodegraph/source/common/NodeGraphOpInput.cpp rename to packages/nodegraph/source/common/operations/NodeGraphOpInput.cpp index 618fa266..f8f2550b 100644 --- a/packages/nodegraph/source/common/NodeGraphOpInput.cpp +++ b/packages/nodegraph/source/common/operations/NodeGraphOpInput.cpp @@ -1,4 +1,4 @@ -#include "nodegraph/NodeGraphOpInput.h" +#include "nodegraph/operations/NodeGraphOpInput.h" #include "logging/Log.h" #include "audio/AudioUtils.h" diff --git a/packages/nodegraph/source/common/NodeGraphOpLogic.cpp b/packages/nodegraph/source/common/operations/NodeGraphOpLogic.cpp similarity index 72% rename from packages/nodegraph/source/common/NodeGraphOpLogic.cpp rename to packages/nodegraph/source/common/operations/NodeGraphOpLogic.cpp index bdbc995d..fd92df2f 100644 --- a/packages/nodegraph/source/common/NodeGraphOpLogic.cpp +++ b/packages/nodegraph/source/common/operations/NodeGraphOpLogic.cpp @@ -1,4 +1,4 @@ -#include "nodegraph/NodeGraphOpLogic.h" +#include "nodegraph/operations/NodeGraphOpLogic.h" #include "logging/Log.h" #include "audio/AudioUtils.h" diff --git a/packages/nodegraph/source/common/NodeGraphOpNumeric.cpp b/packages/nodegraph/source/common/operations/NodeGraphOpNumeric.cpp similarity index 71% rename from packages/nodegraph/source/common/NodeGraphOpNumeric.cpp rename to packages/nodegraph/source/common/operations/NodeGraphOpNumeric.cpp index 5235cb40..98b6df0e 100644 --- a/packages/nodegraph/source/common/NodeGraphOpNumeric.cpp +++ b/packages/nodegraph/source/common/operations/NodeGraphOpNumeric.cpp @@ -1,4 +1,4 @@ -#include "nodegraph/NodeGraphOpNumeric.h" +#include "nodegraph/operations/NodeGraphOpNumeric.h" #include "logging/Log.h" #include "audio/AudioUtils.h" diff --git a/packages/nodegraph/source/common/NodeGraphOpOutput.cpp b/packages/nodegraph/source/common/operations/NodeGraphOpOutput.cpp similarity index 98% rename from packages/nodegraph/source/common/NodeGraphOpOutput.cpp rename to packages/nodegraph/source/common/operations/NodeGraphOpOutput.cpp index 2a7ae886..f15c0228 100644 --- a/packages/nodegraph/source/common/NodeGraphOpOutput.cpp +++ b/packages/nodegraph/source/common/operations/NodeGraphOpOutput.cpp @@ -1,4 +1,4 @@ -#include "nodegraph/NodeGraphOpOutput.h" +#include "nodegraph/operations/NodeGraphOpOutput.h" #include "logging/Log.h" #include "audio/AudioUtils.h" diff --git a/packages/nodegraph/source/common/NodeGraphOpSource.cpp b/packages/nodegraph/source/common/operations/NodeGraphOpSignal.cpp similarity index 65% rename from packages/nodegraph/source/common/NodeGraphOpSource.cpp rename to packages/nodegraph/source/common/operations/NodeGraphOpSignal.cpp index 64eb49ca..d95b4c88 100644 --- a/packages/nodegraph/source/common/NodeGraphOpSource.cpp +++ b/packages/nodegraph/source/common/operations/NodeGraphOpSignal.cpp @@ -1,4 +1,4 @@ -#include "nodegraph/NodeGraphOpSource.h" +#include "nodegraph/operations/NodeGraphOpSignal.h" #include "logging/Log.h" #include "audio/AudioUtils.h" @@ -9,87 +9,150 @@ namespace l::nodegraph { - /* Mathematical operations */ - /*********************************************************************/ - void GraphSourceConstants::Reset() { - switch (mMode) { - case 0: - for (int8_t i = 0; i < 4; i++) { - mNode->SetInputBound(i, InputBound::INPUT_0_TO_1); - } - break; - case 1: - for (int8_t i = 0; i < 4; i++) { - mNode->SetInputBound(i, InputBound::INPUT_NEG_1_POS_1); - } - break; - case 2: - for (int8_t i = 0; i < 4; i++) { - mNode->SetInputBound(i, InputBound::INPUT_0_100); - } - break; - default: - for (int8_t i = 0; i < 4; i++) { - mNode->SetInputBound(i, InputBound::INPUT_UNBOUNDED); - } - break; - } - } + void GraphSignalBase::Reset() { + mFreq = 0.0f; + mVolumeTarget = 0.0f; + mSmooth = 0.5f; + mSignal = 0.0f; + mWave = 0.0f; + mDeltaPhase = 0.0f; + mDeltaTime = 0.0f; + mVolume = 0.0f; + mSamplesUntilUpdate = 0.0f; + mUpdateSamples = 256.0f; + mHPCutoff = 0.5f; + mHPResonance = 0.0001f; + mHPState0 = 0.0f; + mHPState1 = 0.0f; + mHPCutoff = 0.0f; - void GraphSourceConstants::Process(int32_t, std::vector& inputs, std::vector& outputs) { - for (int8_t i = 0; i < mNumOutputs; i++) { - outputs.at(i).mOutput = inputs.at(i).Get(); - } + mNode->SetInput(0, 0.0f); + mNode->SetInput(1, 0.0f); + mNode->SetInput(2, 0.5f); + mNode->SetInput(3, 0.5f); + mNode->SetInput(4, 0.5f); + mNode->SetInput(5, 0.0001f); + mNode->SetInputBound(0, InputBound::INPUT_0_TO_1); + mNode->SetInputBound(1, InputBound::INPUT_CUSTOM, 0.0f, l::math::constants::FLTMAX); + mNode->SetInputBound(2, InputBound::INPUT_0_100); + mNode->SetInputBound(3, InputBound::INPUT_0_TO_1); + mNode->SetInputBound(4, InputBound::INPUT_0_TO_1); + mNode->SetInputBound(5, InputBound::INPUT_0_TO_1); + + ResetSignal(); } - void GraphSourceConstants::Tick(int32_t, float) { - mNode->ProcessSubGraph(1); + void GraphSignalBase::Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) { + float* output0 = &outputs.at(0).GetOutput(numSamples); + + mSamplesUntilUpdate = l::audio::BatchUpdate(mUpdateSamples, mSamplesUntilUpdate, 0, numSamples, + [&]() { + mDeltaTime = 1.0f / 44100.0f; + mReset = inputs.at(0).Get(); + mFreq = inputs.at(1).Get(); + mVolumeTarget = inputs.at(2).Get(); + mSmooth = 0.5f * inputs.at(3).Get(); + + mDeltaPhase = mDeltaTime * mFreq; + float deltaPhaseExpanded = l::math::functions::pow(mDeltaPhase, 0.125f); + mHPCutoff = deltaPhaseExpanded + (1.0f - deltaPhaseExpanded) * inputs.at(4).Get(); + mHPResonance = 1.0f - inputs.at(5).Get(); + + if (mFreq == 0.0f) { + mVolumeTarget = 0.0f; + outputs.at(0).mOutput = 0.0f; + return; + } + if (mReset > 0.5f) { + mVolumeTarget = 0.0f; + } + + UpdateSignal(inputs, outputs); + + }, + [&](int32_t start, int32_t end, bool) { + for (int32_t i = start; i < end; i++) { + float signalTarget = GenerateSignal(mDeltaPhase); + + // highpass filter + { + //float deltaPhase2 = l::math::functions::pow(mDeltaPhase, 0.125f); + float cutoff = mHPCutoff; + cutoff *= cutoff; + float rc = 1.0f - mHPResonance * cutoff; + float v01 = mHPState0 - mHPState1; + mHPState0 += cutoff * (signalTarget - mHPState0 + rc * v01); + mHPState1 += cutoff * v01; + + signalTarget = signalTarget - mHPState1; + } + + mSignal += mSmooth * (signalTarget - mSignal); + mVolume += (1.0f / 256.0f) * (mVolumeTarget - mVolume); + *output0++ = mVolume * mSignal; + } + } + ); } /*********************************************************************/ - void GraphSourceTime::Process(int32_t, std::vector&, std::vector& outputs) { - float rate = 44100.0f; - float phaseChange = 1.0f / rate; - mAudioTime += phaseChange; - outputs.at(0).mOutput = mAudioTime; - outputs.at(1).mOutput = mFrameTime; + void GraphSignalSine2::ResetSignal() { + mNode->SetInput(mNumDefaultInputs + 0, 0.5f); + mNode->SetInput(mNumDefaultInputs + 1, 0.0f); + mNode->SetInputBound(mNumDefaultInputs + 0, InputBound::INPUT_0_TO_1); + mNode->SetInputBound(mNumDefaultInputs + 1, InputBound::INPUT_0_TO_1); } - void GraphSourceTime::Tick(int32_t, float deltaTime) { - mFrameTime += deltaTime; + void GraphSignalSine2::UpdateSignal(std::vector& inputs, std::vector&) { + mFmod = inputs.at(mNumDefaultInputs + 0).Get(); + mPmod = inputs.at(mNumDefaultInputs + 1).Get(); + float fmodRange = 16.0f; + mFmod = mFmod > 0.5f ? 1.0f + fmodRange * (mFmod - 0.5f) : 1.0f / (1.0f + fmodRange * (0.5f - mFmod)); } - void GraphSourceTime::Reset() { - mAudioTime = 0.0f; - mFrameTime = 0.0f; + float GraphSignalSine2::GenerateSignal(float deltaPhase) { + mPhaseFmod += deltaPhase; + mPhaseFmod = l::math::functions::mod(mPhaseFmod, 1.0f); + float modulation = mFmod * (1.0f + 0.5f * l::math::functions::sin(l::math::constants::PI_f * mPhaseFmod * 2.0f)); + + mPhase += deltaPhase * modulation; + mPhase = l::math::functions::mod(mPhase, 1.0f); + + float phaseMod = mPhase + mPmod; + phaseMod = l::math::functions::mod(phaseMod, 1.0f); + + mWave = mSmooth * (mPhase + phaseMod - mWave); + return l::math::functions::sin(l::math::constants::PI_f * mWave); } /*********************************************************************/ - void GraphSourceSine::Reset() { + void GraphSignalSine::Reset() { // { "Freq Hz", "Freq Mod", "Phase Mod", "Reset"}; mPhase = 0.0f; mNode->SetInput(0, 0.0f); mNode->SetInput(1, 0.0f); mNode->SetInput(2, 1.0f); mNode->SetInput(3, 0.0f); - mNode->SetInput(4, 0.0f); + mNode->SetInput(4, 0.5f); + mNode->SetInput(5, 0.0f); mNode->SetInputBound(0, InputBound::INPUT_UNBOUNDED); mNode->SetInputBound(1, InputBound::INPUT_0_100); mNode->SetInputBound(2, InputBound::INPUT_0_TO_2); mNode->SetInputBound(3, InputBound::INPUT_0_TO_1); mNode->SetInputBound(4, InputBound::INPUT_0_TO_1); + mNode->SetInputBound(5, InputBound::INPUT_0_TO_1); } - void GraphSourceSine::Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) { + void GraphSignalSine::Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) { float* output0 = &outputs.at(0).GetOutput(numSamples); mSamplesUntilUpdate = l::audio::BatchUpdate(256.0f, mSamplesUntilUpdate, 0, numSamples, [&]() { mFreq = l::math::functions::max(static_cast(inputs.at(0).Get()), 0.0); mVolume = inputs.at(1).Get(); - mReset = inputs.at(4).Get(); + mReset = inputs.at(5).Get(); if (mFreq == 0.0f) { mVolume = 0.0f; @@ -106,6 +169,7 @@ namespace l::nodegraph { mFmod = static_cast(inputs.at(2).Get()); mPmod = static_cast(inputs.at(3).Get()); + double smooth = 0.5 * static_cast(inputs.at(4).Get()); double fmodRange = 16.0; mFmod = mFmod > 1.0 ? fmodRange * (mFmod - 1.0) : 1.0 / (1.0 + fmodRange * (1.0 - mFmod)); @@ -129,8 +193,8 @@ namespace l::nodegraph { double phaseMod = mPhase + mPmod; phaseMod = l::math::functions::mod(phaseMod, 1.0); - double sine = l::math::functions::sin(l::math::constants::PI * (mPhase + phaseMod)); - + mWave += smooth * (mPhase + phaseMod - mWave); + double sine = l::math::functions::sin(l::math::constants::PI * mWave); /* double phaseDelta = mDeltaTime * mFreq; @@ -145,16 +209,14 @@ namespace l::nodegraph { mVol += (1.0f / 256.0f) * (mVolume - mVol); - mWave += 0.25 * (sine - mWave); - - *output0++ = mVol * static_cast(mWave); + *output0++ = mVol * static_cast(sine); } } ); } /*********************************************************************/ - void GraphSourceSineFM::Reset() { + void GraphSignalSineFM::Reset() { // { "Note", "Volume", "Fmod", "FmodFreq", "FmodVol", "FmodOfs", "Reset"} mPhase = 0.0f; mNode->SetInput(0, 0.0f); @@ -164,7 +226,8 @@ namespace l::nodegraph { mNode->SetInput(4, 0.0f); mNode->SetInput(5, 0.0f); mNode->SetInput(6, 0.0f); - mNode->SetInput(7, 0.0f); + mNode->SetInput(7, 0.5f); + mNode->SetInput(8, 0.0f); mNode->SetInputBound(0, InputBound::INPUT_UNBOUNDED); mNode->SetInputBound(1, InputBound::INPUT_0_100); mNode->SetInputBound(2, InputBound::INPUT_UNBOUNDED); @@ -173,16 +236,17 @@ namespace l::nodegraph { mNode->SetInputBound(5, InputBound::INPUT_UNBOUNDED); mNode->SetInputBound(6, InputBound::INPUT_0_TO_1); mNode->SetInputBound(7, InputBound::INPUT_0_TO_1); + mNode->SetInputBound(8, InputBound::INPUT_0_TO_1); } - void GraphSourceSineFM::Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) { + void GraphSignalSineFM::Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) { float* output0 = &outputs.at(0).GetOutput(numSamples); mSamplesUntilUpdate = l::audio::BatchUpdate(256.0f, mSamplesUntilUpdate, 0, numSamples, [&]() { mFreq = l::math::functions::max(static_cast(inputs.at(0).Get()), 0.0); mVolume = inputs.at(1).Get(); - mReset = inputs.at(7).Get(); + mReset = inputs.at(8).Get(); if (mReset > 0.0f || mVolume + mVol < 0.0000001f) { mPhase = 0.0; @@ -201,6 +265,7 @@ namespace l::nodegraph { mFmodVol = static_cast(inputs.at(4).Get()); mFmodOfs = static_cast(inputs.at(5).Get()); double fmodGain = static_cast(inputs.at(6).Get()); + double smooth = 0.5 * static_cast(inputs.at(7).Get()); for (int32_t i = start; i < end; i++) { double phaseDelta2 = mDeltaTime * mFreq * mFmodFrq; @@ -214,41 +279,44 @@ namespace l::nodegraph { phaseDelta = l::math::functions::clamp(phaseDelta, phaseDelta2, 0.5); mPhase += phaseDelta; mPhase = l::math::functions::mod(mPhase, 1.0); - double waveTarget = l::math::functions::sin(l::math::constants::PI * mPhase * 2.0); + + mWave += smooth * (mPhase - mWave); + double out = l::math::functions::sin(l::math::constants::PI * mWave * 2.0); mVol += (1.0f / 256.0f) * (mVolume - mVol); - mWave += (waveTarget - mWave) * 0.5; - *output0++ = mVol * static_cast(mWave); + *output0++ = mVol * static_cast(out); } } ); } /*********************************************************************/ - void GraphSourceSineFM2::Reset() { + void GraphSignalSineFM2::Reset() { // { "Note", "Volume", "FmodVol", "FmodOfs", "Reset"} mPhase = 0.0f; mNode->SetInput(0, 0.0f); mNode->SetInput(1, 0.5f); mNode->SetInput(2, 0.0f); mNode->SetInput(3, 0.0f); - mNode->SetInput(4, 0.0f); + mNode->SetInput(4, 0.5f); + mNode->SetInput(5, 0.0f); mNode->SetInputBound(0, InputBound::INPUT_UNBOUNDED); mNode->SetInputBound(1, InputBound::INPUT_0_100); mNode->SetInputBound(2, InputBound::INPUT_0_TO_1); mNode->SetInputBound(3, InputBound::INPUT_0_100); mNode->SetInputBound(4, InputBound::INPUT_0_TO_1); + mNode->SetInputBound(5, InputBound::INPUT_0_TO_1); } - void GraphSourceSineFM2::Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) { + void GraphSignalSineFM2::Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) { float* output0 = &outputs.at(0).GetOutput(numSamples); mSamplesUntilUpdate = l::audio::BatchUpdate(256.0f, mSamplesUntilUpdate, 0, numSamples, [&]() { mFreq = l::math::functions::max(static_cast(inputs.at(0).Get()), 0.0); mVolume = inputs.at(1).Get(); - mReset = inputs.at(4).Get(); + mReset = inputs.at(5).Get(); if (mReset > 0.0f || mVolume + mVol < 0.0000001f) { mPhase = 0.0; @@ -265,6 +333,7 @@ namespace l::nodegraph { double fmMod = static_cast(inputs.at(2).Get()); double fmFreq = static_cast(inputs.at(3).Get()); + double smooth = 0.5 * static_cast(inputs.at(4).Get()); double limitFmMod = 1.0 / l::math::functions::max(mFreq / 25.0, 1.0); fmMod = 800.0 * fmMod * fmMod * limitFmMod; @@ -285,25 +354,25 @@ namespace l::nodegraph { mPhase += phaseDelta; mPhase = l::math::functions::mod(mPhase, 1.0); - double waveTarget = l::math::functions::sin(l::math::constants::PI * mPhase * 2.0); + mWave += smooth * (mPhase - mWave); + double out = l::math::functions::sin(l::math::constants::PI * mWave * 2.0); mVol += (1.0f / 256.0f) * (mVolume - mVol); - mWave += (waveTarget - mWave) * 0.5; - *output0++ = mVol * static_cast(mWave); + *output0++ = mVol * static_cast(out); } } ); } /*********************************************************************/ - void GraphSourceSineFM3::Reset() { + void GraphSignalSineFM3::Reset() { // { "Note", "Volume", "Fmod", "Reset"} mPhase = 0.0f; mNode->SetInput(0, 0.0f); mNode->SetInput(1, 0.5f); - mNode->SetInput(2, 0.0f); + mNode->SetInput(2, 0.5f); mNode->SetInput(3, 0.0f); mNode->SetInputBound(0, InputBound::INPUT_UNBOUNDED); mNode->SetInputBound(1, InputBound::INPUT_0_100); @@ -311,14 +380,14 @@ namespace l::nodegraph { mNode->SetInputBound(3, InputBound::INPUT_0_TO_1); } - void GraphSourceSineFM3::Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) { + void GraphSignalSineFM3::Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) { float* output0 = &outputs.at(0).GetOutput(numSamples); mSamplesUntilUpdate = l::audio::BatchUpdate(16.0f, mSamplesUntilUpdate, 0, numSamples, [&]() { mFreq = l::math::functions::max(static_cast(inputs.at(0).Get()), 0.0); mVolume = inputs.at(1).Get(); - mReset = inputs.at(3).Get(); + mReset = inputs.at(4).Get(); if (mReset > 0.0f || mVolume < 0.0000001f) { mPhase = 0.0; @@ -333,7 +402,7 @@ namespace l::nodegraph { [&](int32_t start, int32_t end, bool) { double fmMod = static_cast(inputs.at(2).Get()); - + double smooth = 0.5 * static_cast(inputs.at(3).Get()); double limitFmMod = 1.0 / l::math::functions::max(mFreq / 25.0, 1.0); double fm = 800.0 * fmMod * fmMod * limitFmMod; @@ -346,42 +415,44 @@ namespace l::nodegraph { double phaseDelta = mDeltaTime * mFreq * modulation; mPhase += phaseDelta; mPhase = l::math::functions::mod(mPhase, 1.0); - double waveTarget = l::math::functions::sin(l::math::constants::PI * mPhase * 2.0); + mWave += smooth * (mPhase - mWave); + double out = l::math::functions::sin(l::math::constants::PI * mWave * 2.0); mVol += (1.0f / 256.0f) * (mVolume - mVol); - mWave += (waveTarget - mWave) * 0.5; - *output0++ = mVol * static_cast(mWave); + *output0++ = mVol * static_cast(out); } } ); } /*********************************************************************/ - void GraphSourceSaw::Reset() { + void GraphSignalSaw::Reset() { // { "Freq", "Volume", "Fmod", "Phase", "Reset"}; mPhase = 0.0f; mNode->SetInput(0, 0.0f); mNode->SetInput(1, 0.0f); mNode->SetInput(2, 1.0f); mNode->SetInput(3, 0.0f); - mNode->SetInput(4, 0.0f); + mNode->SetInput(4, 0.5f); + mNode->SetInput(5, 0.0f); mNode->SetInputBound(0, InputBound::INPUT_UNBOUNDED); mNode->SetInputBound(1, InputBound::INPUT_0_100); mNode->SetInputBound(2, InputBound::INPUT_0_TO_2); mNode->SetInputBound(3, InputBound::INPUT_0_TO_1); mNode->SetInputBound(4, InputBound::INPUT_0_TO_1); + mNode->SetInputBound(5, InputBound::INPUT_0_TO_1); } - void GraphSourceSaw::Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) { + void GraphSignalSaw::Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) { float* output0 = &outputs.at(0).GetOutput(numSamples); mSamplesUntilUpdate = l::audio::BatchUpdate(256.0f, mSamplesUntilUpdate, 0, numSamples, [&]() { mFreq = l::math::functions::max(static_cast(inputs.at(0).Get()), 0.0); mVolume = inputs.at(1).Get(); - mReset = inputs.at(4).Get(); + mReset = inputs.at(5).Get(); if (mFreq == 0.0f) { mVolume = 0.0f; @@ -398,6 +469,7 @@ namespace l::nodegraph { mFmod = static_cast(inputs.at(2).Get()); mPmod = static_cast(inputs.at(3).Get()); + double smooth = 0.5 * static_cast(inputs.at(4).Get()); double fmodRange = 16.0; mFmod = mFmod > 1.0 ? fmodRange * (mFmod - 1.0) : 1.0 / (1.0 + fmodRange * (1.0 - mFmod)); @@ -415,12 +487,12 @@ namespace l::nodegraph { double phaseMod = mPhase + mPmod; phaseMod = l::math::functions::mod(phaseMod, 1.0); - double saw = mPhase + phaseMod - 1.0; + double saw = mPhase + phaseMod; + mWave += smooth * (saw - mWave); mVol += (1.0f / 256.0f) * (mVolume - mVol); - mWave += 0.25 * (saw - mWave); - *output0++ = mVol * static_cast(mWave); + *output0++ = mVol * static_cast(mWave - 1.0); } } ); diff --git a/packages/nodegraph/source/common/operations/NodeGraphOpSource.cpp b/packages/nodegraph/source/common/operations/NodeGraphOpSource.cpp new file mode 100644 index 00000000..04539e65 --- /dev/null +++ b/packages/nodegraph/source/common/operations/NodeGraphOpSource.cpp @@ -0,0 +1,69 @@ +#include "nodegraph/operations/NodeGraphOpSource.h" + +#include "logging/Log.h" +#include "audio/AudioUtils.h" + +#include "math/MathFunc.h" + +#include + +namespace l::nodegraph { + + /* Mathematical operations */ + + /*********************************************************************/ + void GraphSourceConstants::Reset() { + switch (mMode) { + case 0: + for (int8_t i = 0; i < 4; i++) { + mNode->SetInputBound(i, InputBound::INPUT_0_TO_1); + } + break; + case 1: + for (int8_t i = 0; i < 4; i++) { + mNode->SetInputBound(i, InputBound::INPUT_NEG_1_POS_1); + } + break; + case 2: + for (int8_t i = 0; i < 4; i++) { + mNode->SetInputBound(i, InputBound::INPUT_0_100); + } + break; + default: + for (int8_t i = 0; i < 4; i++) { + mNode->SetInputBound(i, InputBound::INPUT_UNBOUNDED); + } + break; + } + } + + void GraphSourceConstants::Process(int32_t, std::vector& inputs, std::vector& outputs) { + for (int8_t i = 0; i < mNumOutputs; i++) { + outputs.at(i).mOutput = inputs.at(i).Get(); + } + } + + void GraphSourceConstants::Tick(int32_t, float) { + mNode->ProcessSubGraph(1); + } + + /*********************************************************************/ + void GraphSourceTime::Process(int32_t, std::vector&, std::vector& outputs) { + float rate = 44100.0f; + float phaseChange = 1.0f / rate; + mAudioTime += phaseChange; + + outputs.at(0).mOutput = mAudioTime; + outputs.at(1).mOutput = mFrameTime; + } + + void GraphSourceTime::Tick(int32_t, float deltaTime) { + mFrameTime += deltaTime; + } + + void GraphSourceTime::Reset() { + mAudioTime = 0.0f; + mFrameTime = 0.0f; + } + +} diff --git a/packages/nodegraph/tests/common/NodeGraphSchemaTest.cpp b/packages/nodegraph/tests/common/NodeGraphSchemaTest.cpp index b9181216..85fdd67a 100644 --- a/packages/nodegraph/tests/common/NodeGraphSchemaTest.cpp +++ b/packages/nodegraph/tests/common/NodeGraphSchemaTest.cpp @@ -1,7 +1,7 @@ #include "testing/Test.h" #include "logging/Log.h" -#include "nodegraph/NodeGraph.h" +#include "nodegraph/core/NodeGraphGroup.h" #include "nodegraph/NodeGraphSchema.h" using namespace l; diff --git a/packages/rendering/include/rendering/ui/UICreator.h b/packages/rendering/include/rendering/ui/UICreator.h index d431abbf..038e91e3 100644 --- a/packages/rendering/include/rendering/ui/UICreator.h +++ b/packages/rendering/include/rendering/ui/UICreator.h @@ -13,7 +13,7 @@ #include "implot/implot_internal.h" #include "rendering/ui/UIContainer.h" -#include "nodegraph/NodeGraph.h" +#include "nodegraph/core/NodeGraphBase.h" #include diff --git a/packages/rendering/include/rendering/ui/UINodeEditor.h b/packages/rendering/include/rendering/ui/UINodeEditor.h index 399def08..cccac28a 100644 --- a/packages/rendering/include/rendering/ui/UINodeEditor.h +++ b/packages/rendering/include/rendering/ui/UINodeEditor.h @@ -7,7 +7,6 @@ #include "rendering/ui/UIWindow.h" #include "rendering/ui/UICreator.h" -#include "nodegraph/NodeGraph.h" #include "nodegraph/NodeGraphSchema.h" #include From 37df15927e2a8eaf730abf205952eea620c4028a Mon Sep 17 00:00:00 2001 From: lnd3 Date: Wed, 11 Sep 2024 22:12:50 +0200 Subject: [PATCH 13/42] Fix fm synthesis. --- packages/math/include/math/MathFunc.h | 13 +++++ .../nodegraph/operations/NodeGraphOpEffect.h | 8 ++- .../nodegraph/operations/NodeGraphOpSignal.h | 8 +-- .../common/operations/NodeGraphOpEffect.cpp | 9 ++- .../common/operations/NodeGraphOpSignal.cpp | 56 +++++++++---------- 5 files changed, 56 insertions(+), 38 deletions(-) diff --git a/packages/math/include/math/MathFunc.h b/packages/math/include/math/MathFunc.h index 3b74c50f..eebbb8c2 100644 --- a/packages/math/include/math/MathFunc.h +++ b/packages/math/include/math/MathFunc.h @@ -125,6 +125,19 @@ namespace l::math::functions { } } + + template + T cos(T val) { + if constexpr (std::is_floating_point_v) { + if constexpr (sizeof(T) == 4) { + return cosf(val); + } + else if constexpr (sizeof(T) == 8) { + return ::cos(val); + } + } + } + template T mod(T val, T mod) { if constexpr (std::is_floating_point_v) { diff --git a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpEffect.h b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpEffect.h index d2f998f5..51e35055 100644 --- a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpEffect.h +++ b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpEffect.h @@ -331,11 +331,11 @@ namespace l::nodegraph { /*********************************************************************/ class GraphEffectArpeggio: public NodeGraphOp { public: - std::string defaultInStrings[7] = { "Note On Id", "Note Off Id", "Velocity", "Bpm", "Fmod", "Attack", "Drift Sync"}; + std::string defaultInStrings[8] = { "Note On Id", "Note Off Id", "Velocity", "Bpm", "Fmod", "Attack", "Smooth", "Drift Sync"}; std::string defaultOutStrings[3] = { "Freq", "Volume" }; GraphEffectArpeggio(NodeGraphBase* node) : - NodeGraphOp(node, 7, 2, 0) + NodeGraphOp(node, 8, 2, 0) {} virtual ~GraphEffectArpeggio() = default; @@ -364,7 +364,9 @@ namespace l::nodegraph { float mGainSmoothing = 0.01f; float mGainSmoothingNeg = 0.01f; - float mCurrentNoteFreq = 0.0f; + float mFreqSmoothing = 0.1f; + float mFreqTarget = 0.0f; + float mFreq = 0.0f; std::vector mNotes; int32_t mNoteIndex = 0; }; diff --git a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpSignal.h b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpSignal.h index 87f4bd45..a78978da 100644 --- a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpSignal.h +++ b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpSignal.h @@ -25,7 +25,7 @@ namespace l::nodegraph { class GraphSignalBase : public NodeGraphOp { public: - static const int8_t mNumDefaultInputs = 6; + static const int8_t mNumDefaultInputs = 7; static const int8_t mNumDefaultOutputs = 1; GraphSignalBase(NodeGraphBase* node, std::string_view name, int32_t numInputs = 0, int32_t numOutputs = 0, int32_t numConstants = 0) : @@ -33,7 +33,7 @@ namespace l::nodegraph { mName(name) {} - std::string defaultInStrings[mNumDefaultInputs] = { "Reset", "Freq", "Volume", "Smooth", "Cutoff", "Resonance"}; + std::string defaultInStrings[mNumDefaultInputs] = { "Reset", "Freq", "Volume", "Smooth", "Cutoff", "Resonance", "Phase expansion"}; std::string defaultOutStrings[mNumDefaultOutputs] = { "Out" }; virtual ~GraphSignalBase() = default; @@ -58,7 +58,7 @@ namespace l::nodegraph { virtual std::string_view GetOutputNameExtra(int8_t) { return ""; }; virtual void ResetSignal() {}; virtual void UpdateSignal(std::vector&, std::vector&) {}; - virtual float GenerateSignal(float deltaPhase) = 0; + virtual float GenerateSignal(float deltaTime, float freq, float deltaPhase) = 0; protected: std::string mName; @@ -96,7 +96,7 @@ namespace l::nodegraph { } void ResetSignal() override; void UpdateSignal(std::vector& inputs, std::vector& outputs) override; - float GenerateSignal(float deltaPhase) override; + float GenerateSignal(float deltaTime, float freq, float deltaPhase) override; protected: float mFmod = 0.0f; float mPmod = 0.0f; diff --git a/packages/nodegraph/source/common/operations/NodeGraphOpEffect.cpp b/packages/nodegraph/source/common/operations/NodeGraphOpEffect.cpp index c497b369..a6c92ccb 100644 --- a/packages/nodegraph/source/common/operations/NodeGraphOpEffect.cpp +++ b/packages/nodegraph/source/common/operations/NodeGraphOpEffect.cpp @@ -519,7 +519,7 @@ namespace l::nodegraph { float fmod = inputs.at(4).Get(); float attack = inputs.at(5).Get(); - if (inputs.at(6).Get() > 0.5f) { + if (inputs.at(7).Get() > 0.5f) { mSamplesUntilUpdate = 0.0f; } @@ -565,9 +565,11 @@ namespace l::nodegraph { } else { mNoteIndex = mNoteIndex % mNotes.size(); - mCurrentNoteFreq = l::audio::GetFrequencyFromNote(static_cast(mNotes.at(mNoteIndex))); + mFreqTarget = l::audio::GetFrequencyFromNote(static_cast(mNotes.at(mNoteIndex))); mNoteIndex++; mGainTarget = velocity; + mFreqSmoothing = inputs.at(6).Get(); + mFreqSmoothing *= mFreqSmoothing * 0.5f; } }, [&](int32_t start, int32_t end, bool) { @@ -580,7 +582,8 @@ namespace l::nodegraph { mGain += mGainSmoothingNeg * (-l::math::smooth::smootPolyh3(-delta)); } - outputs.at(0).mOutput = mCurrentNoteFreq; + mFreq += mFreqSmoothing * (mFreqTarget - mFreq); + outputs.at(0).mOutput = mFreq; outputs.at(1).mOutput = mGain; } }); diff --git a/packages/nodegraph/source/common/operations/NodeGraphOpSignal.cpp b/packages/nodegraph/source/common/operations/NodeGraphOpSignal.cpp index d95b4c88..2f0f2d58 100644 --- a/packages/nodegraph/source/common/operations/NodeGraphOpSignal.cpp +++ b/packages/nodegraph/source/common/operations/NodeGraphOpSignal.cpp @@ -20,7 +20,7 @@ namespace l::nodegraph { mDeltaTime = 0.0f; mVolume = 0.0f; mSamplesUntilUpdate = 0.0f; - mUpdateSamples = 256.0f; + mUpdateSamples = 16.0f; mHPCutoff = 0.5f; mHPResonance = 0.0001f; mHPState0 = 0.0f; @@ -30,15 +30,17 @@ namespace l::nodegraph { mNode->SetInput(0, 0.0f); mNode->SetInput(1, 0.0f); mNode->SetInput(2, 0.5f); - mNode->SetInput(3, 0.5f); - mNode->SetInput(4, 0.5f); - mNode->SetInput(5, 0.0001f); + mNode->SetInput(3, 1.0f); + mNode->SetInput(4, 0.0f); + mNode->SetInput(5, 0.0f); + mNode->SetInput(6, 0.5f); mNode->SetInputBound(0, InputBound::INPUT_0_TO_1); mNode->SetInputBound(1, InputBound::INPUT_CUSTOM, 0.0f, l::math::constants::FLTMAX); mNode->SetInputBound(2, InputBound::INPUT_0_100); mNode->SetInputBound(3, InputBound::INPUT_0_TO_1); mNode->SetInputBound(4, InputBound::INPUT_0_TO_1); mNode->SetInputBound(5, InputBound::INPUT_0_TO_1); + mNode->SetInputBound(6, InputBound::INPUT_0_TO_1); ResetSignal(); } @@ -53,18 +55,15 @@ namespace l::nodegraph { mFreq = inputs.at(1).Get(); mVolumeTarget = inputs.at(2).Get(); mSmooth = 0.5f * inputs.at(3).Get(); + float phaseExpansion = inputs.at(6).Get(); mDeltaPhase = mDeltaTime * mFreq; - float deltaPhaseExpanded = l::math::functions::pow(mDeltaPhase, 0.125f); + //float deltaPhaseExpanded = l::math::functions::pow(mDeltaPhase, phaseExpansion); + float deltaPhaseExpanded = l::math::functions::pow(mDeltaPhase, 0.4f - phaseExpansion * mDeltaPhase * 6.0f); mHPCutoff = deltaPhaseExpanded + (1.0f - deltaPhaseExpanded) * inputs.at(4).Get(); mHPResonance = 1.0f - inputs.at(5).Get(); - if (mFreq == 0.0f) { - mVolumeTarget = 0.0f; - outputs.at(0).mOutput = 0.0f; - return; - } - if (mReset > 0.5f) { + if (mFreq == 0.0f || mReset > 0.5f) { mVolumeTarget = 0.0f; } @@ -73,7 +72,7 @@ namespace l::nodegraph { }, [&](int32_t start, int32_t end, bool) { for (int32_t i = start; i < end; i++) { - float signalTarget = GenerateSignal(mDeltaPhase); + float signalTarget = GenerateSignal(mDeltaTime, mFreq, mDeltaPhase); // highpass filter { @@ -99,32 +98,33 @@ namespace l::nodegraph { /*********************************************************************/ void GraphSignalSine2::ResetSignal() { - mNode->SetInput(mNumDefaultInputs + 0, 0.5f); + mNode->SetInput(mNumDefaultInputs + 0, 0.0f); mNode->SetInput(mNumDefaultInputs + 1, 0.0f); mNode->SetInputBound(mNumDefaultInputs + 0, InputBound::INPUT_0_TO_1); mNode->SetInputBound(mNumDefaultInputs + 1, InputBound::INPUT_0_TO_1); + mUpdateSamples = 16.0f; } void GraphSignalSine2::UpdateSignal(std::vector& inputs, std::vector&) { mFmod = inputs.at(mNumDefaultInputs + 0).Get(); mPmod = inputs.at(mNumDefaultInputs + 1).Get(); - float fmodRange = 16.0f; - mFmod = mFmod > 0.5f ? 1.0f + fmodRange * (mFmod - 0.5f) : 1.0f / (1.0f + fmodRange * (0.5f - mFmod)); + mFmod *= 0.25f * 0.25f * 0.5f * 44100.0f / l::math::functions::max(mFreq, 1.0f); } - float GraphSignalSine2::GenerateSignal(float deltaPhase) { + float GraphSignalSine2::GenerateSignal(float, float, float deltaPhase) { mPhaseFmod += deltaPhase; mPhaseFmod = l::math::functions::mod(mPhaseFmod, 1.0f); - float modulation = mFmod * (1.0f + 0.5f * l::math::functions::sin(l::math::constants::PI_f * mPhaseFmod * 2.0f)); + float modulation = l::math::functions::cos(l::math::constants::PI_f * mPhaseFmod * 2.0f); - mPhase += deltaPhase * modulation; - mPhase = l::math::functions::mod(mPhase, 1.0f); + mPhase = mPhaseFmod; + mPhase += mFmod * modulation; + mPhase -= l::math::functions::floor(mPhase); - float phaseMod = mPhase + mPmod; - phaseMod = l::math::functions::mod(phaseMod, 1.0f); + float phaseMod = mPhaseFmod + mPmod; + phaseMod -= l::math::functions::floor(phaseMod); - mWave = mSmooth * (mPhase + phaseMod - mWave); - return l::math::functions::sin(l::math::constants::PI_f * mWave); + //mWave = mSmooth * (mPhase + phaseMod - mWave); + return 0.5f * (l::math::functions::sin(l::math::constants::PI_f * mPhase * 2.0f) + l::math::functions::sin(l::math::constants::PI_f * phaseMod * 2.0f)); } /*********************************************************************/ @@ -137,7 +137,7 @@ namespace l::nodegraph { mNode->SetInput(3, 0.0f); mNode->SetInput(4, 0.5f); mNode->SetInput(5, 0.0f); - mNode->SetInputBound(0, InputBound::INPUT_UNBOUNDED); + mNode->SetInputBound(0, InputBound::INPUT_CUSTOM, 0.0f, l::math::constants::FLTMAX); mNode->SetInputBound(1, InputBound::INPUT_0_100); mNode->SetInputBound(2, InputBound::INPUT_0_TO_2); mNode->SetInputBound(3, InputBound::INPUT_0_TO_1); @@ -228,7 +228,7 @@ namespace l::nodegraph { mNode->SetInput(6, 0.0f); mNode->SetInput(7, 0.5f); mNode->SetInput(8, 0.0f); - mNode->SetInputBound(0, InputBound::INPUT_UNBOUNDED); + mNode->SetInputBound(0, InputBound::INPUT_CUSTOM, 0.0f, l::math::constants::FLTMAX); mNode->SetInputBound(1, InputBound::INPUT_0_100); mNode->SetInputBound(2, InputBound::INPUT_UNBOUNDED); mNode->SetInputBound(3, InputBound::INPUT_UNBOUNDED); @@ -301,7 +301,7 @@ namespace l::nodegraph { mNode->SetInput(3, 0.0f); mNode->SetInput(4, 0.5f); mNode->SetInput(5, 0.0f); - mNode->SetInputBound(0, InputBound::INPUT_UNBOUNDED); + mNode->SetInputBound(0, InputBound::INPUT_CUSTOM, 0.0f, l::math::constants::FLTMAX); mNode->SetInputBound(1, InputBound::INPUT_0_100); mNode->SetInputBound(2, InputBound::INPUT_0_TO_1); mNode->SetInputBound(3, InputBound::INPUT_0_100); @@ -374,7 +374,7 @@ namespace l::nodegraph { mNode->SetInput(1, 0.5f); mNode->SetInput(2, 0.5f); mNode->SetInput(3, 0.0f); - mNode->SetInputBound(0, InputBound::INPUT_UNBOUNDED); + mNode->SetInputBound(0, InputBound::INPUT_CUSTOM, 0.0f, l::math::constants::FLTMAX); mNode->SetInputBound(1, InputBound::INPUT_0_100); mNode->SetInputBound(2, InputBound::INPUT_0_TO_1); mNode->SetInputBound(3, InputBound::INPUT_0_TO_1); @@ -437,7 +437,7 @@ namespace l::nodegraph { mNode->SetInput(3, 0.0f); mNode->SetInput(4, 0.5f); mNode->SetInput(5, 0.0f); - mNode->SetInputBound(0, InputBound::INPUT_UNBOUNDED); + mNode->SetInputBound(0, InputBound::INPUT_CUSTOM, 0.0f, l::math::constants::FLTMAX); mNode->SetInputBound(1, InputBound::INPUT_0_100); mNode->SetInputBound(2, InputBound::INPUT_0_TO_2); mNode->SetInputBound(3, InputBound::INPUT_0_TO_1); From c5fd047c013de5a60217ee3c89b811158aaff039 Mon Sep 17 00:00:00 2001 From: lnd3 Date: Wed, 11 Sep 2024 22:23:26 +0200 Subject: [PATCH 14/42] Fix phase shift. --- .../nodegraph/source/common/operations/NodeGraphOpSignal.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/nodegraph/source/common/operations/NodeGraphOpSignal.cpp b/packages/nodegraph/source/common/operations/NodeGraphOpSignal.cpp index 2f0f2d58..3174f7db 100644 --- a/packages/nodegraph/source/common/operations/NodeGraphOpSignal.cpp +++ b/packages/nodegraph/source/common/operations/NodeGraphOpSignal.cpp @@ -120,7 +120,7 @@ namespace l::nodegraph { mPhase += mFmod * modulation; mPhase -= l::math::functions::floor(mPhase); - float phaseMod = mPhaseFmod + mPmod; + float phaseMod = mPhaseFmod + mPmod * modulation; phaseMod -= l::math::functions::floor(phaseMod); //mWave = mSmooth * (mPhase + phaseMod - mWave); From a8ece91339e6d4307c28e92ecda6ff4376141abe Mon Sep 17 00:00:00 2001 From: lnd3 Date: Thu, 12 Sep 2024 16:20:10 +0200 Subject: [PATCH 15/42] Add custom band limited saw generator and chamberlin two-pole low pass multi output filter. --- packages/math/include/math/MathFunc.h | 49 +++++- .../include/nodegraph/NodeGraphSchema.h | 2 + .../nodegraph/operations/NodeGraphOpFilter.h | 44 ++++++ .../nodegraph/operations/NodeGraphOpSignal.h | 143 +++++++++++++++++- .../source/common/NodeGraphSchema.cpp | 6 + .../common/operations/NodeGraphOpFilter.cpp | 48 ++++++ .../common/operations/NodeGraphOpSignal.cpp | 91 +++++------ .../source/common/ui/UIContainer.cpp | 2 + .../rendering/source/common/ui/UICreator.cpp | 5 +- .../rendering/source/common/ui/UIVisitors.cpp | 2 +- 10 files changed, 335 insertions(+), 57 deletions(-) diff --git a/packages/math/include/math/MathFunc.h b/packages/math/include/math/MathFunc.h index eebbb8c2..e97043b8 100644 --- a/packages/math/include/math/MathFunc.h +++ b/packages/math/include/math/MathFunc.h @@ -125,7 +125,6 @@ namespace l::math::functions { } } - template T cos(T val) { if constexpr (std::is_floating_point_v) { @@ -138,6 +137,54 @@ namespace l::math::functions { } } + template + T tan(T val) { + if constexpr (std::is_floating_point_v) { + if constexpr (sizeof(T) == 4) { + return tanf(val); + } + else if constexpr (sizeof(T) == 8) { + return ::tan(val); + } + } + } + + template + T asin(T val) { + if constexpr (std::is_floating_point_v) { + if constexpr (sizeof(T) == 4) { + return asinf(val); + } + else if constexpr (sizeof(T) == 8) { + return ::asin(val); + } + } + } + + template + T acos(T val) { + if constexpr (std::is_floating_point_v) { + if constexpr (sizeof(T) == 4) { + return acosf(val); + } + else if constexpr (sizeof(T) == 8) { + return ::acos(val); + } + } + } + + template + T atan(T val) { + if constexpr (std::is_floating_point_v) { + if constexpr (sizeof(T) == 4) { + return atanf(val); + } + else if constexpr (sizeof(T) == 8) { + return ::atan(val); + } + } + } + template T mod(T val, T mod) { if constexpr (std::is_floating_point_v) { diff --git a/packages/nodegraph/include/nodegraph/NodeGraphSchema.h b/packages/nodegraph/include/nodegraph/NodeGraphSchema.h index 9e6e25be..4eeb98aa 100644 --- a/packages/nodegraph/include/nodegraph/NodeGraphSchema.h +++ b/packages/nodegraph/include/nodegraph/NodeGraphSchema.h @@ -59,6 +59,7 @@ namespace l::nodegraph { RegisterNodeType("Logic", 102, "Xor"); RegisterNodeType("Filter", 150, "Lowpass"); RegisterNodeType("Filter", 151, "Highpass"); + RegisterNodeType("Filter", 152, "Chamberlin two-pole (4 mode)"); RegisterNodeType("Output", 200, "Debug"); RegisterNodeType("Output", 201, "Speaker"); RegisterNodeType("Output", 202, "Plot"); @@ -84,6 +85,7 @@ namespace l::nodegraph { RegisterNodeType("Signal", 353, "Sine FM 3"); RegisterNodeType("Signal", 354, "Saw"); RegisterNodeType("Signal", 355, "Sine 2"); + RegisterNodeType("Signal", 356, "Saw 2"); } diff --git a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpFilter.h b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpFilter.h index e7009ef5..c1ec2616 100644 --- a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpFilter.h +++ b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpFilter.h @@ -86,5 +86,49 @@ namespace l::nodegraph { float mState1 = 0.0f; }; + /*********************************************************************/ + // source: https://www.musicdsp.org/en/latest/Filters/23-state-variable.html + class GraphFilterChamberlain2pole : public NodeGraphOp { + public: + std::string defaultInStrings[4] = { "In", "Cutoff", "Resonance", "Mode"}; + std::string defaultOutStrings[1] = { "Out"}; + + GraphFilterChamberlain2pole(NodeGraphBase* node) : + NodeGraphOp(node, 4, 1) + { + mState.resize(4); + } + + virtual ~GraphFilterChamberlain2pole() = default; + virtual void Reset() override; + virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; + virtual bool IsDataVisible(int8_t) override { return true; } + virtual bool IsDataEditable(int8_t channel) override { return channel > 0 ? 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 "Chamberlin two-pole"; + } + protected: + float mSamplesUntilUpdate = 0.0f; + float mUpdateSamples = 16.0f; + + float mInputValuePrev = 0.0f; + float mCutoff = 0.0f; + float mResonance = 0.0f; + + float mSampleRate = 44100.0f; + float mFreq = 0.0f; + float mScale = 0.0f; + + std::vector mState; + }; + } diff --git a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpSignal.h b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpSignal.h index a78978da..5989fad6 100644 --- a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpSignal.h +++ b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpSignal.h @@ -25,7 +25,7 @@ namespace l::nodegraph { class GraphSignalBase : public NodeGraphOp { public: - static const int8_t mNumDefaultInputs = 7; + static const int8_t mNumDefaultInputs = 4; static const int8_t mNumDefaultOutputs = 1; GraphSignalBase(NodeGraphBase* node, std::string_view name, int32_t numInputs = 0, int32_t numOutputs = 0, int32_t numConstants = 0) : @@ -33,7 +33,7 @@ namespace l::nodegraph { mName(name) {} - std::string defaultInStrings[mNumDefaultInputs] = { "Reset", "Freq", "Volume", "Smooth", "Cutoff", "Resonance", "Phase expansion"}; + std::string defaultInStrings[mNumDefaultInputs] = { "Reset", "Freq", "Volume", "Smooth"}; std::string defaultOutStrings[mNumDefaultOutputs] = { "Out" }; virtual ~GraphSignalBase() = default; @@ -73,12 +73,6 @@ namespace l::nodegraph { float mVolumeTarget = 0.0f; float mSamplesUntilUpdate = 0.0f; float mUpdateSamples = 256.0f; - - // high pass - float mHPCutoff = 0.5f; - float mHPResonance = 0.0001f; - float mHPState0 = 0.0f; - float mHPState1 = 0.0f; }; /*********************************************************************/ @@ -104,6 +98,139 @@ namespace l::nodegraph { float mPhaseFmod = 0.0f; }; + /*********************************************************************/ + struct LowpassType { + double x1 = 0.0; + double y1 = 0.0; + double a = 0.0; + double b = 0.0; + }; + + struct WaveformBlit { + double phase = 0.0; /* phase accumulator */ + double aNQ = 0.0; /* attenuation at nyquist */ + double curcps = 0.0; /* current frequency, updated once per cycle */ + double curper = 0.0; /* current period, updated once per cycle */ + LowpassType leaky; /* leaky integrator */ + double N = 0.0; /* # partials */ + double a = 0.0; /* dsf parameter which controls roll-off */ + double aN = 0.0; /* former to the N */ + }; + + class GraphSignalSaw2 : public GraphSignalBase { + public: + GraphSignalSaw2(NodeGraphBase* node) : + GraphSignalBase(node, "Saw 2", 2) + {} + std::string extraString[2] = { "Attenuation", "Cutoff" }; + + virtual ~GraphSignalSaw2() = default; + virtual std::string_view GetInputNameExtra(int8_t extraInputChannel) override { + if (extraInputChannel < 2) return extraString[static_cast(extraInputChannel)]; + return ""; + } + void ResetSignal() override; + void UpdateSignal(std::vector& inputs, std::vector& outputs) override; + float GenerateSignal(float deltaTime, float freq, float deltaPhase) override; + + void InitSaw(WaveformBlit* b, double aNQ, double cutoff) + { + b->phase = 0.0; + b->aNQ = aNQ; + b->curcps = 0.0; + b->curper = 0.0; + InitLowpass(&b->leaky, cutoff); + } + + void UpdateSaw(WaveformBlit* b, double aNQ, double cutoff) { + b->aNQ = aNQ; + UpdateLowpass(&b->leaky, cutoff + 0.00001); + } + + /* Returns a sawtooth computed from a leaky integration + * of a DSF bandlimited impulse train. + * + * cps (cycles per sample) is the fundamental + * frequency: 0 -> 0.5 == 0 -> nyquist + */ + + double ProcessSaw(WaveformBlit* b, double cps) { + double P2, beta, Nbeta, cosbeta, n, d, blit, saw; + + if (b->phase >= 1.0 || b->curcps == 0.0) + { + /* New cycle, update frequency and everything + * that depends on it + */ + + if (b->phase >= 1.0) + b->phase -= 1.0; + double cpsClamped = l::math::functions::clamp(cps, 2.0 / 44100, 0.5); + b->curcps = cpsClamped; /* this cycle\'s frequency */ + b->curper = 1.0 / cpsClamped; /* this cycle\'s period */ + + P2 = b->curper / 2.0; + b->N = 1.0 + l::math::functions::floor(P2); /* # of partials incl. dc */ + + /* find the roll-off parameter which gives + * the desired attenuation at nyquist + */ + + b->a = l::math::functions::pow(b->aNQ, 1.0 / P2); + b->aN = l::math::functions::pow(b->a, b->N); + } + + beta = 2.0 * l::math::constants::PI * b->phase; + + Nbeta = b->N * beta; + cosbeta = l::math::functions::cos(beta); + + /* The dsf blit is scaled by 1 / period to give approximately the same + * peak-to-peak over a wide range of frequencies. + */ + + n = 1.0 - + b->aN * l::math::functions::cos(Nbeta) - + b->a * (cosbeta - b->aN * l::math::functions::cos(Nbeta - beta)); + d = b->curper * (1.0 + b->a * (-2.0 * cosbeta + b->a)); + + b->phase += b->curcps; /* update phase */ + + blit = n / d - b->curcps; /* This division can only fail if |a| == 1.0 + * Subtracting the fundamental frq rids of DC + */ + + saw = ProcessLowpass(&b->leaky, blit); /* shape blit spectrum into a saw */ + + return 4.0 * saw; + } + + void InitLowpass(LowpassType* lp, double cutoff) { + lp->x1 = lp->y1 = 0.0; + UpdateLowpass(lp, cutoff); + } + + void UpdateLowpass(LowpassType* lp, double cutoff) { + double Omega = l::math::functions::atan(l::math::constants::PI * cutoff); + lp->a = -(1.0 - Omega) / (1.0 + Omega); + lp->b = (1.0 - lp->b) / 2.0; + } + + double ProcessLowpass(LowpassType* lp, double x) { + double y; + y = lp->b * (x + lp->x1) - lp->a * lp->y1; + lp->x1 = x; + lp->y1 = y; + return y; + } + + protected: + double mAttenuation = 0.0f; + double mCutoff = 0.0f; + + WaveformBlit mSaw; + }; + /*********************************************************************/ class GraphSignalSine : public NodeGraphOp { public: diff --git a/packages/nodegraph/source/common/NodeGraphSchema.cpp b/packages/nodegraph/source/common/NodeGraphSchema.cpp index 527bf93a..282b1245 100644 --- a/packages/nodegraph/source/common/NodeGraphSchema.cpp +++ b/packages/nodegraph/source/common/NodeGraphSchema.cpp @@ -79,6 +79,9 @@ namespace l::nodegraph { case 151: node = mMainNodeGraph.NewNode(OutputType::Default); break; + case 152: + node = mMainNodeGraph.NewNode(OutputType::Default); + break; case 200: node = mMainNodeGraph.NewNode(OutputType::ExternalOutput); break; @@ -154,6 +157,9 @@ namespace l::nodegraph { case 355: node = mMainNodeGraph.NewNode(OutputType::Default); break; + case 356: + node = mMainNodeGraph.NewNode(OutputType::Default); + break; default: diff --git a/packages/nodegraph/source/common/operations/NodeGraphOpFilter.cpp b/packages/nodegraph/source/common/operations/NodeGraphOpFilter.cpp index 0bc5e164..e0648649 100644 --- a/packages/nodegraph/source/common/operations/NodeGraphOpFilter.cpp +++ b/packages/nodegraph/source/common/operations/NodeGraphOpFilter.cpp @@ -59,4 +59,52 @@ namespace l::nodegraph { outputs.at(0).mOutput = inputValue - mState1; } + + /*********************************************************************/ + void GraphFilterChamberlain2pole::Reset() { + for (int32_t i = 0; i < 4; i++) { + mState.at(i) = 0.0f; + } + + mSamplesUntilUpdate = 0.0f; + mUpdateSamples = 4.0f; + + mNode->SetInput(1, 0.99f); + mNode->SetInput(2, 0.01f); + mNode->SetInput(3, 0.0f); + mNode->SetInputBound(1, InputBound::INPUT_0_TO_1); + mNode->SetInputBound(2, InputBound::INPUT_0_TO_1); + mNode->SetInputBound(3, InputBound::INPUT_0_TO_1); + } + + void GraphFilterChamberlain2pole::Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) { + auto input = &inputs.at(0).Get(numSamples); + + auto mode = static_cast(3.0f * inputs.at(3).Get() + 0.5f); + auto output0 = &outputs.at(0).GetOutput(numSamples); + + mSamplesUntilUpdate = l::audio::BatchUpdate(mUpdateSamples, mSamplesUntilUpdate, 0, numSamples, + [&]() { + mCutoff = inputs.at(1).Get(); + mResonance = 1.0f - inputs.at(2).Get(); + + mFreq = l::math::functions::sin(l::math::constants::PI_f * mCutoff * mCutoff / 2.0f); + mScale = l::math::functions::sqrt(mResonance); + }, + [&](int32_t start, int32_t end, bool) { + for (int32_t i = start; i < end; i++) { + float inputValue = *input++; + float inputValueInbetween = (mInputValuePrev + inputValue) * 0.5f; + for (int32_t oversample = 0; oversample < 2; oversample++) { + mState.at(0) = mState.at(0) + mFreq * mState.at(2); + mState.at(1) = mScale * (oversample == 0 ? inputValueInbetween : inputValue) - mState.at(0) - mResonance * mState.at(2); + mState.at(2) = mFreq * mState.at(1) + mState.at(2); + mState.at(3) = mState.at(1) + mState.at(0); + } + *output0++ = mState.at(mode); + mInputValuePrev = inputValue; + } + } + ); + } } diff --git a/packages/nodegraph/source/common/operations/NodeGraphOpSignal.cpp b/packages/nodegraph/source/common/operations/NodeGraphOpSignal.cpp index 3174f7db..9aead8a7 100644 --- a/packages/nodegraph/source/common/operations/NodeGraphOpSignal.cpp +++ b/packages/nodegraph/source/common/operations/NodeGraphOpSignal.cpp @@ -20,27 +20,16 @@ namespace l::nodegraph { mDeltaTime = 0.0f; mVolume = 0.0f; mSamplesUntilUpdate = 0.0f; - mUpdateSamples = 16.0f; - mHPCutoff = 0.5f; - mHPResonance = 0.0001f; - mHPState0 = 0.0f; - mHPState1 = 0.0f; - mHPCutoff = 0.0f; + mUpdateSamples = 4.0f; mNode->SetInput(0, 0.0f); - mNode->SetInput(1, 0.0f); + mNode->SetInput(1, 441.0047f); mNode->SetInput(2, 0.5f); - mNode->SetInput(3, 1.0f); - mNode->SetInput(4, 0.0f); - mNode->SetInput(5, 0.0f); - mNode->SetInput(6, 0.5f); + mNode->SetInput(3, 0.5f); mNode->SetInputBound(0, InputBound::INPUT_0_TO_1); mNode->SetInputBound(1, InputBound::INPUT_CUSTOM, 0.0f, l::math::constants::FLTMAX); mNode->SetInputBound(2, InputBound::INPUT_0_100); mNode->SetInputBound(3, InputBound::INPUT_0_TO_1); - mNode->SetInputBound(4, InputBound::INPUT_0_TO_1); - mNode->SetInputBound(5, InputBound::INPUT_0_TO_1); - mNode->SetInputBound(6, InputBound::INPUT_0_TO_1); ResetSignal(); } @@ -50,22 +39,26 @@ namespace l::nodegraph { mSamplesUntilUpdate = l::audio::BatchUpdate(mUpdateSamples, mSamplesUntilUpdate, 0, numSamples, [&]() { - mDeltaTime = 1.0f / 44100.0f; mReset = inputs.at(0).Get(); mFreq = inputs.at(1).Get(); - mVolumeTarget = inputs.at(2).Get(); - mSmooth = 0.5f * inputs.at(3).Get(); - float phaseExpansion = inputs.at(6).Get(); - - mDeltaPhase = mDeltaTime * mFreq; - //float deltaPhaseExpanded = l::math::functions::pow(mDeltaPhase, phaseExpansion); - float deltaPhaseExpanded = l::math::functions::pow(mDeltaPhase, 0.4f - phaseExpansion * mDeltaPhase * 6.0f); - mHPCutoff = deltaPhaseExpanded + (1.0f - deltaPhaseExpanded) * inputs.at(4).Get(); - mHPResonance = 1.0f - inputs.at(5).Get(); - if (mFreq == 0.0f || mReset > 0.5f) { + if (mReset > 0.5f) { mVolumeTarget = 0.0f; } + else if (mFreq <= 0.0f) { + mFreq = 0.0f; + mVolumeTarget *= mVolumeTarget; + if (mVolumeTarget < 0.00001f) { + mVolumeTarget = 0.0f; + } + } + else { + mVolumeTarget = inputs.at(2).Get(); + } + + mSmooth = inputs.at(3).Get(); + mDeltaTime = 1.0f / 44100.0f; + mDeltaPhase = mDeltaTime * mFreq; UpdateSignal(inputs, outputs); @@ -73,22 +66,8 @@ namespace l::nodegraph { [&](int32_t start, int32_t end, bool) { for (int32_t i = start; i < end; i++) { float signalTarget = GenerateSignal(mDeltaTime, mFreq, mDeltaPhase); - - // highpass filter - { - //float deltaPhase2 = l::math::functions::pow(mDeltaPhase, 0.125f); - float cutoff = mHPCutoff; - cutoff *= cutoff; - float rc = 1.0f - mHPResonance * cutoff; - float v01 = mHPState0 - mHPState1; - mHPState0 += cutoff * (signalTarget - mHPState0 + rc * v01); - mHPState1 += cutoff * v01; - - signalTarget = signalTarget - mHPState1; - } - mSignal += mSmooth * (signalTarget - mSignal); - mVolume += (1.0f / 256.0f) * (mVolumeTarget - mVolume); + mVolume += 0.5f * (mVolumeTarget - mVolume); *output0++ = mVolume * mSignal; } } @@ -102,7 +81,7 @@ namespace l::nodegraph { mNode->SetInput(mNumDefaultInputs + 1, 0.0f); mNode->SetInputBound(mNumDefaultInputs + 0, InputBound::INPUT_0_TO_1); mNode->SetInputBound(mNumDefaultInputs + 1, InputBound::INPUT_0_TO_1); - mUpdateSamples = 16.0f; + mUpdateSamples = 4.0f; } void GraphSignalSine2::UpdateSignal(std::vector& inputs, std::vector&) { @@ -114,19 +93,41 @@ namespace l::nodegraph { float GraphSignalSine2::GenerateSignal(float, float, float deltaPhase) { mPhaseFmod += deltaPhase; mPhaseFmod = l::math::functions::mod(mPhaseFmod, 1.0f); - float modulation = l::math::functions::cos(l::math::constants::PI_f * mPhaseFmod * 2.0f); + float modulation = 0.5f * (mFmod + mFmod * l::math::functions::cos(l::math::constants::PI_f * mPhaseFmod * 2.0f)); + //float modulation = (0.5f - deltaPhase) + (mFmod) * 0.5f * l::math::functions::cos(l::math::constants::PI_f * mPhaseFmod * 2.0f); + mPhase = mPhaseFmod; - mPhase += mFmod * modulation; + mPhase += modulation; mPhase -= l::math::functions::floor(mPhase); - float phaseMod = mPhaseFmod + mPmod * modulation; + float phaseMod = mPhaseFmod + mPmod * modulation * 4.0f; phaseMod -= l::math::functions::floor(phaseMod); - //mWave = mSmooth * (mPhase + phaseMod - mWave); return 0.5f * (l::math::functions::sin(l::math::constants::PI_f * mPhase * 2.0f) + l::math::functions::sin(l::math::constants::PI_f * phaseMod * 2.0f)); } + /*********************************************************************/ + + void GraphSignalSaw2::ResetSignal() { + mNode->SetInput(mNumDefaultInputs + 0, 0.0f); + mNode->SetInput(mNumDefaultInputs + 1, 0.0f); + mNode->SetInputBound(mNumDefaultInputs + 0, InputBound::INPUT_0_TO_1); + mNode->SetInputBound(mNumDefaultInputs + 1, InputBound::INPUT_0_TO_1); + mUpdateSamples = 4.0f; + } + + void GraphSignalSaw2::UpdateSignal(std::vector& inputs, std::vector&) { + mAttenuation = inputs.at(mNumDefaultInputs + 0).Get(); + mCutoff = inputs.at(mNumDefaultInputs + 1).Get(); + + UpdateSaw(&mSaw, 0.00001f + 0.99999f * mAttenuation * mAttenuation * mAttenuation, mCutoff * mCutoff); + } + + float GraphSignalSaw2::GenerateSignal(float, float, float deltaPhase) { + return static_cast(ProcessSaw(&mSaw, static_cast(deltaPhase))); + } + /*********************************************************************/ void GraphSignalSine::Reset() { // { "Freq Hz", "Freq Mod", "Phase Mod", "Reset"}; diff --git a/packages/rendering/source/common/ui/UIContainer.cpp b/packages/rendering/source/common/ui/UIContainer.cpp index 78a860cb..c86698b1 100644 --- a/packages/rendering/source/common/ui/UIContainer.cpp +++ b/packages/rendering/source/common/ui/UIContainer.cpp @@ -259,9 +259,11 @@ namespace l::ui { break; case UISplitMode::AppendH: current.mPosition.x += content->GetSize().x * mLayoutArea.mScale; + current.mSize.x -= content->GetSize().x * mLayoutArea.mScale; break; case UISplitMode::AppendV: current.mPosition.y += content->GetSize().y * mLayoutArea.mScale; + current.mSize.y -= content->GetSize().y * mLayoutArea.mScale; break; case UISplitMode::EqualResizeH: break; diff --git a/packages/rendering/source/common/ui/UICreator.cpp b/packages/rendering/source/common/ui/UICreator.cpp index f8050eb1..4ab77f4a 100644 --- a/packages/rendering/source/common/ui/UICreator.cpp +++ b/packages/rendering/source/common/ui/UICreator.cpp @@ -90,12 +90,13 @@ namespace l::ui { } if (node.GetOutputType() == l::nodegraph::OutputType::ExternalVisualOutput) { - auto row = CreateContainer(uiStorage, l::ui::UIContainer_DrawFlag, l::ui::UIRenderType::Rect, l::ui::UIAlignH::Left, l::ui::UIAlignV::Top, l::ui::UILayoutH::Parent, l::ui::UILayoutV::Parent); + auto row = CreateContainer(uiStorage, l::ui::UIContainer_DrawFlag, l::ui::UIRenderType::Rect, l::ui::UIAlignH::Left, l::ui::UIAlignV::Bottom, l::ui::UILayoutH::Parent, l::ui::UILayoutV::Parent); + row->SetPosition(ImVec2(0.0f, 0.0f)); row->GetContainerArea().mMargin = ioSize; node4->Add(row); float estimatedWidth = 0.0f; - auto plot = CreateContainer(uiStorage, l::ui::UIContainer_DrawFlag, l::ui::UIRenderType::NodeOutputGraph, l::ui::UIAlignH::Center, l::ui::UIAlignV::Middle, l::ui::UILayoutH::Parent, l::ui::UILayoutV::Parent); + auto plot = CreateContainer(uiStorage, l::ui::UIContainer_DrawFlag, l::ui::UIRenderType::NodeOutputGraph, l::ui::UIAlignH::Center, l::ui::UIAlignV::Bottom, l::ui::UILayoutH::Parent, l::ui::UILayoutV::Parent); plot->SetPosition(ImVec2(estimatedWidth, 0.0f)); plot->SetSize(ImVec2(100, 100)); plot->SetNodeId(node.GetId()); diff --git a/packages/rendering/source/common/ui/UIVisitors.cpp b/packages/rendering/source/common/ui/UIVisitors.cpp index 4e9d9546..992da034 100644 --- a/packages/rendering/source/common/ui/UIVisitors.cpp +++ b/packages/rendering/source/common/ui/UIVisitors.cpp @@ -379,7 +379,7 @@ namespace l::ui { float xpart1 = i / static_cast(nodeValueCount); float xpart2 = (i+1) / static_cast(nodeValueCount); ImVec2 graphP1 = ImVec2(startPos.x + size.x * xpart1, startPos.y + 0.5f * nodeValues[i] * size.y); - ImVec2 graphP2 = ImVec2(startPos.x + size.x * xpart2, startPos.y + 0.5f * nodeValues[i] * size.y); + ImVec2 graphP2 = ImVec2(startPos.x + size.x * xpart2, startPos.y + 0.5f * nodeValues[i+1] * size.y); mDrawList->AddLine(graphP1, graphP2, color, 2.0f * container.GetScale()); } } From a6f5ef30617f1ae0076f5b3ea259db2a604923d9 Mon Sep 17 00:00:00 2001 From: lnd3 Date: Fri, 13 Sep 2024 03:35:37 +0200 Subject: [PATCH 16/42] Add rwa interpolator. Create a filter base for simpler filter implementations. --- packages/audio/include/audio/AudioUtils.h | 41 +++++ .../nodegraph/operations/NodeGraphOpFilter.h | 130 +++++++------- .../nodegraph/operations/NodeGraphOpSignal.h | 25 +-- .../common/operations/NodeGraphOpFilter.cpp | 163 ++++++++++-------- .../common/operations/NodeGraphOpSignal.cpp | 91 ++++++---- 5 files changed, 271 insertions(+), 179 deletions(-) diff --git a/packages/audio/include/audio/AudioUtils.h b/packages/audio/include/audio/AudioUtils.h index 2a979bcb..274901c8 100644 --- a/packages/audio/include/audio/AudioUtils.h +++ b/packages/audio/include/audio/AudioUtils.h @@ -1,5 +1,7 @@ #pragma once +#include "math/MathFunc.h" + #include namespace l::audio { @@ -9,4 +11,43 @@ namespace l::audio { double GetPhaseModifier(double note, double modifier); float GetRCAFactor(float numSamples, float limit = 0.01f); float BatchUpdate(float updateSamples, float samplesLeft, int32_t start, int32_t end, std::function update, std::function process); + + template + class FilterRWA { + public: + + FilterRWA() : + mValue(static_cast(0)), + mTargetValue(static_cast(0)) + {} + ~FilterRWA() {} + + bool SnapAt(T value = static_cast(0), T proximity = static_cast(0.00001)) { + if (l::math::functions::abs(mValue - value) < proximity) { + mValue = value; + return true; + } + return false; + } + + FilterRWA& SetConvergence(T smooth = static_cast(0.005)) { + mSmooth = smooth; + return *this; + } + + FilterRWA& SetTarget(T target) { + mTargetValue = target; + return *this; + } + + T Next() { + mValue += mSmooth * (mTargetValue - mValue); + return mValue; + } + + protected: + T mSmooth; + T mValue; + T mTargetValue; + }; } diff --git a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpFilter.h b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpFilter.h index c1ec2616..7081bfd2 100644 --- a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpFilter.h +++ b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpFilter.h @@ -23,111 +23,119 @@ namespace l::nodegraph { /* Stateful filtering operations */ - - /*********************************************************************/ - class GraphFilterLowpass : public NodeGraphOp { + class GraphFilterBase : public NodeGraphOp { public: - std::string defaultInStrings[3] = { "Data", "Cutoff", "Resonance"}; - std::string defaultOutStrings[1] = { "Out" }; - GraphFilterLowpass(NodeGraphBase* node) : - NodeGraphOp(node, 3, 1) + static const int8_t mNumDefaultInputs = 4; + static const int8_t mNumDefaultOutputs = 1; + + GraphFilterBase(NodeGraphBase* node, std::string_view name, int32_t numInputs = 0, int32_t numOutputs = 0, int32_t numConstants = 0) : + NodeGraphOp(node, mNumDefaultInputs + numInputs, mNumDefaultOutputs + numOutputs, numConstants), + mName(name) {} - virtual ~GraphFilterLowpass() = default; - virtual void Reset() override; - virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; + std::string defaultInStrings[mNumDefaultInputs] = { "Sync", "In", "Cutoff", "Resonance" }; + std::string defaultOutStrings[mNumDefaultOutputs] = { "Out" }; + + virtual ~GraphFilterBase() = default; + virtual void Reset() override final; + virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override final; virtual bool IsDataVisible(int8_t) override { return true; } - virtual bool IsDataEditable(int8_t channel) override { return channel > 0 ? true : false; } - virtual std::string_view GetInputName(int8_t inputChannel) override { - return defaultInStrings[inputChannel]; + virtual bool IsDataEditable(int8_t) override { return true; } + virtual std::string_view GetInputName(int8_t inputChannel) override final { + if (inputChannel >= mNumDefaultInputs) return GetInputNameExtra(inputChannel - mNumDefaultInputs); + if (inputChannel >= 0) return defaultInStrings[static_cast(inputChannel)]; + return ""; } - - virtual std::string_view GetOutputName(int8_t outputChannel) override { + virtual std::string_view GetOutputName(int8_t outputChannel) override final { + if (outputChannel >= mNumDefaultOutputs) return GetOutputNameExtra(outputChannel - mNumDefaultOutputs); return defaultOutStrings[outputChannel]; } - virtual std::string_view GetName() override { - return "Lowpass"; + return mName; } + + virtual std::string_view GetInputNameExtra(int8_t) { return ""; }; + virtual std::string_view GetOutputNameExtra(int8_t) { return ""; }; + virtual void ResetInput() {}; + virtual void ResetSignal() {}; + virtual void UpdateSignal(std::vector&, std::vector&) {}; + virtual float ProcessSignal(float input, float cutoff, float resonance) = 0; + protected: + std::string mName; + float mReset = 0.0f; + float mSamplesUntilUpdate = 0.0f; + float mUpdateSamples = 16.0f; + + float mCutoff = 0.0f; + float mResonance = 0.0f; + float mCutoffCPS = 0.0f; // cutoff cycles per sample + l::audio::FilterRWA mCutoffFilter; + l::audio::FilterRWA mResonanceFilter; + }; + + /*********************************************************************/ + class GraphFilterLowpass : public GraphFilterBase { + public: + GraphFilterLowpass(NodeGraphBase* node) : + GraphFilterBase(node, "Lowpass") + {} + virtual ~GraphFilterLowpass() = default; + + virtual void ResetInput() override; + virtual void ResetSignal() override; + virtual float ProcessSignal(float input, float cutoff, float resonance) override; protected: + float mInputValuePrev = 0.0f; float mState0 = 0.0f; float mState1 = 0.0f; }; /*********************************************************************/ - class GraphFilterHighpass : public NodeGraphOp { + class GraphFilterHighpass : public GraphFilterBase { public: - std::string defaultInStrings[3] = { "Data", "Cutoff", "Resonance" }; - std::string defaultOutStrings[1] = { "Out" }; - GraphFilterHighpass(NodeGraphBase* node) : - NodeGraphOp(node, 3, 1) + GraphFilterBase(node, "Highpass") {} virtual ~GraphFilterHighpass() = default; - virtual void Reset() override; - virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; - virtual bool IsDataVisible(int8_t) override { return true; } - virtual bool IsDataEditable(int8_t channel) override { return channel > 0 ? 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 "Highpass"; - } + virtual void ResetInput() override; + virtual void ResetSignal() override; + virtual float ProcessSignal(float input, float cutoff, float resonance) override; protected: + float mInputValuePrev = 0.0f; float mState0 = 0.0f; float mState1 = 0.0f; }; /*********************************************************************/ // source: https://www.musicdsp.org/en/latest/Filters/23-state-variable.html - class GraphFilterChamberlain2pole : public NodeGraphOp { + class GraphFilterChamberlain2pole : public GraphFilterBase { public: - std::string defaultInStrings[4] = { "In", "Cutoff", "Resonance", "Mode"}; - std::string defaultOutStrings[1] = { "Out"}; - GraphFilterChamberlain2pole(NodeGraphBase* node) : - NodeGraphOp(node, 4, 1) + GraphFilterBase(node, "Chamberlin two-pole", 1) { mState.resize(4); } virtual ~GraphFilterChamberlain2pole() = default; - virtual void Reset() override; - virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; - virtual bool IsDataVisible(int8_t) override { return true; } - virtual bool IsDataEditable(int8_t channel) override { return channel > 0 ? 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 void ResetInput() override; + virtual void ResetSignal() override; + virtual void UpdateSignal(std::vector&, std::vector&) override; + virtual float ProcessSignal(float input, float cutoff, float resonance) override; - virtual std::string_view GetName() override { - return "Chamberlin two-pole"; + virtual std::string_view GetInputNameExtra(int8_t) override { + return "Mode"; } protected: - float mSamplesUntilUpdate = 0.0f; - float mUpdateSamples = 16.0f; - float mInputValuePrev = 0.0f; - float mCutoff = 0.0f; - float mResonance = 0.0f; - - float mSampleRate = 44100.0f; - float mFreq = 0.0f; float mScale = 0.0f; + int32_t mMode = 0; std::vector mState; + l::audio::FilterRWA mScaleFilter; }; } diff --git a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpSignal.h b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpSignal.h index 5989fad6..508191ce 100644 --- a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpSignal.h +++ b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpSignal.h @@ -25,7 +25,7 @@ namespace l::nodegraph { class GraphSignalBase : public NodeGraphOp { public: - static const int8_t mNumDefaultInputs = 4; + static const int8_t mNumDefaultInputs = 5; static const int8_t mNumDefaultOutputs = 1; GraphSignalBase(NodeGraphBase* node, std::string_view name, int32_t numInputs = 0, int32_t numOutputs = 0, int32_t numConstants = 0) : @@ -33,7 +33,7 @@ namespace l::nodegraph { mName(name) {} - std::string defaultInStrings[mNumDefaultInputs] = { "Reset", "Freq", "Volume", "Smooth"}; + std::string defaultInStrings[mNumDefaultInputs] = { "Sync", "Rate", "Freq", "Volume", "Smooth"}; std::string defaultOutStrings[mNumDefaultOutputs] = { "Out" }; virtual ~GraphSignalBase() = default; @@ -56,23 +56,23 @@ namespace l::nodegraph { virtual std::string_view GetInputNameExtra(int8_t) { return ""; }; virtual std::string_view GetOutputNameExtra(int8_t) { return ""; }; + virtual void ResetInput() {}; virtual void ResetSignal() {}; virtual void UpdateSignal(std::vector&, std::vector&) {}; - virtual float GenerateSignal(float deltaTime, float freq, float deltaPhase) = 0; + virtual float ProcessSignal(float deltaTime, float freq) = 0; protected: std::string mName; float mReset = 0.0f; float mFreq = 0.0f; - float mVolume = 0.0f; float mSmooth = 0.5f; - float mSignal = 0.0f; - float mWave = 0.0f; - float mDeltaPhase = 0.0f; float mDeltaTime = 0.0f; float mVolumeTarget = 0.0f; float mSamplesUntilUpdate = 0.0f; - float mUpdateSamples = 256.0f; + float mUpdateSamples = 16.0f; + + l::audio::FilterRWA mFilterSignal; + l::audio::FilterRWA mFilterVolume; }; /*********************************************************************/ @@ -88,14 +88,18 @@ namespace l::nodegraph { if(extraInputChannel < 2) return extraString[static_cast(extraInputChannel)]; return ""; } + void ResetInput() override; void ResetSignal() override; void UpdateSignal(std::vector& inputs, std::vector& outputs) override; - float GenerateSignal(float deltaTime, float freq, float deltaPhase) override; + float ProcessSignal(float deltaTime, float freq) override; protected: float mFmod = 0.0f; float mPmod = 0.0f; float mPhase = 0.0f; float mPhaseFmod = 0.0f; + + l::audio::FilterRWA mFilterFmod; + l::audio::FilterRWA mFilterPmod; }; /*********************************************************************/ @@ -129,9 +133,10 @@ namespace l::nodegraph { if (extraInputChannel < 2) return extraString[static_cast(extraInputChannel)]; return ""; } + void ResetInput() override; void ResetSignal() override; void UpdateSignal(std::vector& inputs, std::vector& outputs) override; - float GenerateSignal(float deltaTime, float freq, float deltaPhase) override; + float ProcessSignal(float deltaTime, float freq) override; void InitSaw(WaveformBlit* b, double aNQ, double cutoff) { diff --git a/packages/nodegraph/source/common/operations/NodeGraphOpFilter.cpp b/packages/nodegraph/source/common/operations/NodeGraphOpFilter.cpp index e0648649..aa075a02 100644 --- a/packages/nodegraph/source/common/operations/NodeGraphOpFilter.cpp +++ b/packages/nodegraph/source/common/operations/NodeGraphOpFilter.cpp @@ -10,101 +10,124 @@ namespace l::nodegraph { /* Stateful filtering operations */ + /*********************************************************************/ + void GraphFilterBase::Reset() { + mReset = 0.0f; + mSamplesUntilUpdate = 0.0f; - /*********************************************************************/ - void GraphFilterLowpass::Reset() { - mState0 = 0.0f; - mState1 = 0.0f; - mNode->SetInput(1, 0.99f); - mNode->SetInput(2, 0.01f); - mNode->SetInputBound(1, InputBound::INPUT_0_TO_1); + mCutoff = 0.0f; + mResonance = 0.0f; + mCutoffCPS = 0.0f; + + mNode->SetInput(0, 0.0f); + mNode->SetInput(2, 1.0f); + mNode->SetInput(3, 0.0f); + mNode->SetInputBound(0, InputBound::INPUT_0_TO_1); mNode->SetInputBound(2, InputBound::INPUT_0_TO_1); + mNode->SetInputBound(3, InputBound::INPUT_0_TO_1); + + ResetInput(); + ResetSignal(); } - void GraphFilterLowpass::Process(int32_t, std::vector& inputs, std::vector& outputs) { - float inputValue = inputs.at(0).Get(); - float cutoff = inputs.at(1).Get(); - float resonance = 1.0f - inputs.at(2).Get(); + void GraphFilterBase::Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) { + auto input = &inputs.at(1).Get(numSamples); + auto output = &outputs.at(0).GetOutput(numSamples); - cutoff *= cutoff; - float rc = 1.0f - resonance * cutoff; + mSamplesUntilUpdate = l::audio::BatchUpdate(mUpdateSamples, mSamplesUntilUpdate, 0, numSamples, + [&]() { + mReset = inputs.at(0).Get(); + if (mReset > 0.5f) { + mNode->SetInput(1, 0.0f, numSamples); + } - mState0 = rc * mState0 - cutoff * (mState1 + inputValue); - mState1 = rc * mState1 + cutoff * mState0; + mCutoff = inputs.at(2).Get(); + mCutoffCPS = l::math::functions::sin(l::math::constants::PI_f * mCutoff * mCutoff / 2.0f); + mResonance = 1.0f - inputs.at(3).Get(); - outputs.at(0).mOutput = -mState1; - } + mCutoffFilter.SetConvergence().SetTarget(mCutoffCPS).SnapAt(); + mResonanceFilter.SetConvergence().SetTarget(mResonance).SnapAt(); + UpdateSignal(inputs, outputs); + }, + [&](int32_t start, int32_t end, bool) { + for (int32_t i = start; i < end; i++) { + float inputValue = *input++; + float signal = ProcessSignal(inputValue, mCutoffFilter.Next(), mResonanceFilter.Next()); + *output++ = signal; + } + } + ); + } /*********************************************************************/ - void GraphFilterHighpass::Reset() { + void GraphFilterLowpass::ResetInput() { + } + + void GraphFilterLowpass::ResetSignal() { mState0 = 0.0f; mState1 = 0.0f; - mNode->SetInput(1, 0.99f); - mNode->SetInput(2, 0.01f); - mNode->SetInputBound(1, InputBound::INPUT_0_TO_1); - mNode->SetInputBound(2, InputBound::INPUT_0_TO_1); } - void GraphFilterHighpass::Process(int32_t, std::vector& inputs, std::vector& outputs) { - float inputValue = inputs.at(0).Get(); - float cutoff = inputs.at(1).Get(); - float resonance = 1.0f - inputs.at(2).Get(); + float GraphFilterLowpass::ProcessSignal(float input, float cutoff, float resonance) { + float inputValueInbetween = (mInputValuePrev + input) * 0.5f; + for (int32_t oversample = 0; oversample < 2; oversample++) { + mState0 = resonance * mState0 - cutoff * (mState1 + (oversample == 0 ? inputValueInbetween : input)); + mState1 = resonance * mState1 + cutoff * mState0; + } + mInputValuePrev = input; + return -mState1; + } - cutoff *= cutoff; - float rc = 1.0f - resonance * cutoff; + /*********************************************************************/ + void GraphFilterHighpass::ResetInput() { + } - float v01 = mState0 - mState1; - mState0 += cutoff * (inputValue - mState0 + rc * v01); - mState1 += cutoff * v01; + void GraphFilterHighpass::ResetSignal() { + mState0 = 0.0f; + mState1 = 0.0f; + } - outputs.at(0).mOutput = inputValue - mState1; + float GraphFilterHighpass::ProcessSignal(float input, float cutoff, float resonance) { + float inputValueInbetween = (mInputValuePrev + input) * 0.5f; + for (int32_t oversample = 0; oversample < 2; oversample++) { + float v01 = mState0 - mState1; + mState0 += cutoff * ((oversample == 0 ? inputValueInbetween : input) - mState0 + resonance * v01); + mState1 += cutoff * v01; + } + mInputValuePrev = input; + return input - mState1; } /*********************************************************************/ - void GraphFilterChamberlain2pole::Reset() { + void GraphFilterChamberlain2pole::ResetInput() { + mNode->SetInput(mNumDefaultInputs + 0, 0.0f); + mNode->SetInputBound(mNumDefaultInputs + 0, InputBound::INPUT_0_TO_1); + } + + void GraphFilterChamberlain2pole::ResetSignal() { for (int32_t i = 0; i < 4; i++) { mState.at(i) = 0.0f; } - - mSamplesUntilUpdate = 0.0f; - mUpdateSamples = 4.0f; - - mNode->SetInput(1, 0.99f); - mNode->SetInput(2, 0.01f); - mNode->SetInput(3, 0.0f); - mNode->SetInputBound(1, InputBound::INPUT_0_TO_1); - mNode->SetInputBound(2, InputBound::INPUT_0_TO_1); - mNode->SetInputBound(3, InputBound::INPUT_0_TO_1); } - void GraphFilterChamberlain2pole::Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) { - auto input = &inputs.at(0).Get(numSamples); - - auto mode = static_cast(3.0f * inputs.at(3).Get() + 0.5f); - auto output0 = &outputs.at(0).GetOutput(numSamples); + void GraphFilterChamberlain2pole::UpdateSignal(std::vector& inputs, std::vector&) { + mMode = static_cast(3.0f * inputs.at(mNumDefaultInputs + 0).Get() + 0.5f); + mScale = l::math::functions::sqrt(mResonance); + mScaleFilter.SetConvergence().SetTarget(mScale).SnapAt(); + } - mSamplesUntilUpdate = l::audio::BatchUpdate(mUpdateSamples, mSamplesUntilUpdate, 0, numSamples, - [&]() { - mCutoff = inputs.at(1).Get(); - mResonance = 1.0f - inputs.at(2).Get(); + float GraphFilterChamberlain2pole::ProcessSignal(float input, float cutoff, float resonance) { + float inputValueInbetween = (mInputValuePrev + input) * 0.5f; + float scale = mScaleFilter.Next(); + cutoff *= 0.5f; + for (int32_t oversample = 0; oversample < 2; oversample++) { + mState.at(0) = mState.at(0) + cutoff * mState.at(2); + mState.at(1) = scale * (oversample == 0 ? inputValueInbetween : input) - mState.at(0) - resonance * mState.at(2); + mState.at(2) = cutoff * mState.at(1) + mState.at(2); + mState.at(3) = mState.at(1) + mState.at(0); + } + mInputValuePrev = input; - mFreq = l::math::functions::sin(l::math::constants::PI_f * mCutoff * mCutoff / 2.0f); - mScale = l::math::functions::sqrt(mResonance); - }, - [&](int32_t start, int32_t end, bool) { - for (int32_t i = start; i < end; i++) { - float inputValue = *input++; - float inputValueInbetween = (mInputValuePrev + inputValue) * 0.5f; - for (int32_t oversample = 0; oversample < 2; oversample++) { - mState.at(0) = mState.at(0) + mFreq * mState.at(2); - mState.at(1) = mScale * (oversample == 0 ? inputValueInbetween : inputValue) - mState.at(0) - mResonance * mState.at(2); - mState.at(2) = mFreq * mState.at(1) + mState.at(2); - mState.at(3) = mState.at(1) + mState.at(0); - } - *output0++ = mState.at(mode); - mInputValuePrev = inputValue; - } - } - ); + return mState.at(mMode); } } diff --git a/packages/nodegraph/source/common/operations/NodeGraphOpSignal.cpp b/packages/nodegraph/source/common/operations/NodeGraphOpSignal.cpp index 9aead8a7..afc1ec30 100644 --- a/packages/nodegraph/source/common/operations/NodeGraphOpSignal.cpp +++ b/packages/nodegraph/source/common/operations/NodeGraphOpSignal.cpp @@ -14,61 +14,59 @@ namespace l::nodegraph { mFreq = 0.0f; mVolumeTarget = 0.0f; mSmooth = 0.5f; - mSignal = 0.0f; - mWave = 0.0f; - mDeltaPhase = 0.0f; mDeltaTime = 0.0f; - mVolume = 0.0f; mSamplesUntilUpdate = 0.0f; - mUpdateSamples = 4.0f; mNode->SetInput(0, 0.0f); - mNode->SetInput(1, 441.0047f); - mNode->SetInput(2, 0.5f); + mNode->SetInput(1, 256.0f); + mNode->SetInput(2, 0.0f); // 441.0047f is stable in plot when debugging mNode->SetInput(3, 0.5f); + mNode->SetInput(4, 0.5f); mNode->SetInputBound(0, InputBound::INPUT_0_TO_1); - mNode->SetInputBound(1, InputBound::INPUT_CUSTOM, 0.0f, l::math::constants::FLTMAX); - mNode->SetInputBound(2, InputBound::INPUT_0_100); - mNode->SetInputBound(3, InputBound::INPUT_0_TO_1); + mNode->SetInputBound(1, InputBound::INPUT_CUSTOM, 1.0f, 44100.0f); + mNode->SetInputBound(2, InputBound::INPUT_CUSTOM, 0.0f, l::math::constants::FLTMAX); + mNode->SetInputBound(3, InputBound::INPUT_0_100); + mNode->SetInputBound(4, InputBound::INPUT_0_TO_1); - ResetSignal(); + ResetInput(); } void GraphSignalBase::Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) { + float sync = inputs.at(0).Get(); + if (sync > 0.5f) { + mSamplesUntilUpdate = 0; + } + float* output0 = &outputs.at(0).GetOutput(numSamples); mSamplesUntilUpdate = l::audio::BatchUpdate(mUpdateSamples, mSamplesUntilUpdate, 0, numSamples, [&]() { - mReset = inputs.at(0).Get(); - mFreq = inputs.at(1).Get(); + mUpdateSamples = inputs.at(1).Get(); + mFreq = inputs.at(2).Get(); - if (mReset > 0.5f) { - mVolumeTarget = 0.0f; - } - else if (mFreq <= 0.0f) { + if (mFreq <= 0.0f) { mFreq = 0.0f; mVolumeTarget *= mVolumeTarget; - if (mVolumeTarget < 0.00001f) { - mVolumeTarget = 0.0f; - } } else { - mVolumeTarget = inputs.at(2).Get(); + mVolumeTarget = inputs.at(3).Get(); + } + mSmooth = inputs.at(4).Get(); + + if (mFilterVolume.SetConvergence().SetTarget(mVolumeTarget).SnapAt()) { + mVolumeTarget = 0.0f; + ResetSignal(); } + mFilterSignal.SetConvergence(mSmooth); - mSmooth = inputs.at(3).Get(); mDeltaTime = 1.0f / 44100.0f; - mDeltaPhase = mDeltaTime * mFreq; UpdateSignal(inputs, outputs); - }, [&](int32_t start, int32_t end, bool) { for (int32_t i = start; i < end; i++) { - float signalTarget = GenerateSignal(mDeltaTime, mFreq, mDeltaPhase); - mSignal += mSmooth * (signalTarget - mSignal); - mVolume += 0.5f * (mVolumeTarget - mVolume); - *output0++ = mVolume * mSignal; + float signalTarget = ProcessSignal(mDeltaTime, mFreq); + *output0++ = mFilterVolume.Next() * mFilterSignal.SetTarget(signalTarget).Next(); } } ); @@ -76,24 +74,33 @@ namespace l::nodegraph { /*********************************************************************/ - void GraphSignalSine2::ResetSignal() { + void GraphSignalSine2::ResetInput() { mNode->SetInput(mNumDefaultInputs + 0, 0.0f); mNode->SetInput(mNumDefaultInputs + 1, 0.0f); mNode->SetInputBound(mNumDefaultInputs + 0, InputBound::INPUT_0_TO_1); mNode->SetInputBound(mNumDefaultInputs + 1, InputBound::INPUT_0_TO_1); - mUpdateSamples = 4.0f; + mUpdateSamples = 16.0f; + } + + void GraphSignalSine2::ResetSignal() { + mPhaseFmod = 0.0f; + mPhase = 0.0f; } void GraphSignalSine2::UpdateSignal(std::vector& inputs, std::vector&) { mFmod = inputs.at(mNumDefaultInputs + 0).Get(); mPmod = inputs.at(mNumDefaultInputs + 1).Get(); mFmod *= 0.25f * 0.25f * 0.5f * 44100.0f / l::math::functions::max(mFreq, 1.0f); + + mFilterFmod.SetConvergence().SetTarget(mFmod); + mFilterPmod.SetConvergence().SetTarget(mPmod); } - float GraphSignalSine2::GenerateSignal(float, float, float deltaPhase) { - mPhaseFmod += deltaPhase; + float GraphSignalSine2::ProcessSignal(float deltaTime, float freq) { + mPhaseFmod += deltaTime * freq; mPhaseFmod = l::math::functions::mod(mPhaseFmod, 1.0f); - float modulation = 0.5f * (mFmod + mFmod * l::math::functions::cos(l::math::constants::PI_f * mPhaseFmod * 2.0f)); + float fmod = mFilterFmod.Next(); + float modulation = 0.5f * (fmod + fmod * l::math::functions::cos(l::math::constants::PI_f * mPhaseFmod * 2.0f)); //float modulation = (0.5f - deltaPhase) + (mFmod) * 0.5f * l::math::functions::cos(l::math::constants::PI_f * mPhaseFmod * 2.0f); @@ -101,7 +108,7 @@ namespace l::nodegraph { mPhase += modulation; mPhase -= l::math::functions::floor(mPhase); - float phaseMod = mPhaseFmod + mPmod * modulation * 4.0f; + float phaseMod = mPhaseFmod + mFilterPmod.Next() * modulation * 4.0f; phaseMod -= l::math::functions::floor(phaseMod); return 0.5f * (l::math::functions::sin(l::math::constants::PI_f * mPhase * 2.0f) + l::math::functions::sin(l::math::constants::PI_f * phaseMod * 2.0f)); @@ -109,12 +116,16 @@ namespace l::nodegraph { /*********************************************************************/ - void GraphSignalSaw2::ResetSignal() { + void GraphSignalSaw2::ResetInput() { mNode->SetInput(mNumDefaultInputs + 0, 0.0f); mNode->SetInput(mNumDefaultInputs + 1, 0.0f); mNode->SetInputBound(mNumDefaultInputs + 0, InputBound::INPUT_0_TO_1); mNode->SetInputBound(mNumDefaultInputs + 1, InputBound::INPUT_0_TO_1); - mUpdateSamples = 4.0f; + mUpdateSamples = 16.0f; + } + + void GraphSignalSaw2::ResetSignal() { + InitSaw(&mSaw, 0.0, 0.0); } void GraphSignalSaw2::UpdateSignal(std::vector& inputs, std::vector&) { @@ -124,14 +135,18 @@ namespace l::nodegraph { UpdateSaw(&mSaw, 0.00001f + 0.99999f * mAttenuation * mAttenuation * mAttenuation, mCutoff * mCutoff); } - float GraphSignalSaw2::GenerateSignal(float, float, float deltaPhase) { - return static_cast(ProcessSaw(&mSaw, static_cast(deltaPhase))); + float GraphSignalSaw2::ProcessSignal(float deltaTime, float freq) { + return static_cast(ProcessSaw(&mSaw, static_cast(deltaTime) * static_cast(freq))); } /*********************************************************************/ void GraphSignalSine::Reset() { // { "Freq Hz", "Freq Mod", "Phase Mod", "Reset"}; mPhase = 0.0f; + mPhaseFmod = 0.0f; + mWave = 0.0f; + mVol = 0.0f; + mNode->SetInput(0, 0.0f); mNode->SetInput(1, 0.0f); mNode->SetInput(2, 1.0f); From c6181bc9ba238fc6207389b79637c16facb2a1c2 Mon Sep 17 00:00:00 2001 From: lnd3 Date: Sat, 14 Sep 2024 15:12:21 +0200 Subject: [PATCH 17/42] Simplify node operations and limit how they can be modified making them easier to implement and harder to get wrong. General improvements. Remove node constants and use inputs without input as constants instead. --- CMakeLists.txt | 2 +- packages/audio/include/audio/AudioUtils.h | 11 + packages/hid/CMakeLists.txt | 1 + packages/hid/include/hid/KeyboardPiano.h | 16 +- packages/hid/source/common/KeyboardPiano.cpp | 2 +- .../include/nodegraph/NodeGraphSchema.h | 7 - .../include/nodegraph/core/NodeGraphBase.h | 70 ++-- .../include/nodegraph/core/NodeGraphGroup.h | 34 +- .../include/nodegraph/core/NodeGraphInput.h | 6 +- .../include/nodegraph/core/NodeGraphOutput.h | 8 +- .../nodegraph/operations/NodeGraphOpEffect.h | 303 ++++++++---------- .../nodegraph/operations/NodeGraphOpFilter.h | 46 +-- .../nodegraph/operations/NodeGraphOpInput.h | 131 ++++---- .../nodegraph/operations/NodeGraphOpLogic.h | 39 ++- .../nodegraph/operations/NodeGraphOpNumeric.h | 113 +++---- .../nodegraph/operations/NodeGraphOpOutput.h | 65 ++-- .../nodegraph/operations/NodeGraphOpSignal.h | 217 ++++++------- .../nodegraph/operations/NodeGraphOpSource.h | 76 +++-- .../source/common/NodeGraphSchema.cpp | 23 +- .../source/common/core/NodeGraphBase.cpp | 130 ++++---- .../source/common/core/NodeGraphGroup.cpp | 39 +-- .../source/common/core/NodeGraphInput.cpp | 28 ++ .../source/common/core/NodeGraphOutput.cpp | 8 +- .../common/operations/NodeGraphOpEffect.cpp | 150 +-------- .../common/operations/NodeGraphOpFilter.cpp | 4 +- .../common/operations/NodeGraphOpInput.cpp | 67 +--- .../common/operations/NodeGraphOpOutput.cpp | 115 ++----- .../common/operations/NodeGraphOpSignal.cpp | 124 +------ .../common/operations/NodeGraphOpSource.cpp | 52 +-- .../tests/common/NodeGraphSchemaTest.cpp | 30 +- .../rendering/source/common/ui/UICreator.cpp | 7 +- .../rendering/source/common/ui/UIVisitors.cpp | 4 +- 32 files changed, 714 insertions(+), 1214 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d74c0c8a..9c95548e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,8 +24,8 @@ else() meta memory math - hid audio + hid nodegraph concurrency diff --git a/packages/audio/include/audio/AudioUtils.h b/packages/audio/include/audio/AudioUtils.h index 274901c8..2324718a 100644 --- a/packages/audio/include/audio/AudioUtils.h +++ b/packages/audio/include/audio/AudioUtils.h @@ -5,6 +5,16 @@ #include namespace l::audio { + + class INoteProcessor { + public: + virtual ~INoteProcessor() = default; + virtual void NoteOn(int32_t, int32_t = 127) {} + virtual void NoteOff() {} + virtual void NoteOff(int32_t) {} + virtual void NoteSustain(bool) {} + }; + extern const float gNoNote_f; extern const int32_t gNoNote; float GetFrequencyFromNote(float note); @@ -17,6 +27,7 @@ namespace l::audio { public: FilterRWA() : + mSmooth(static_cast(0.005)), mValue(static_cast(0)), mTargetValue(static_cast(0)) {} diff --git a/packages/hid/CMakeLists.txt b/packages/hid/CMakeLists.txt index f7f9beba..b78d7740 100644 --- a/packages/hid/CMakeLists.txt +++ b/packages/hid/CMakeLists.txt @@ -5,6 +5,7 @@ set(deps testing math + audio ) bs_generate_package(hid "tier1" "${deps}" "") diff --git a/packages/hid/include/hid/KeyboardPiano.h b/packages/hid/include/hid/KeyboardPiano.h index 30ad4873..33f2952e 100644 --- a/packages/hid/include/hid/KeyboardPiano.h +++ b/packages/hid/include/hid/KeyboardPiano.h @@ -3,6 +3,7 @@ #include "logging/LoggingAll.h" #include "hid/KeyState.h" +#include "audio/AudioUtils.h" #include #include @@ -10,15 +11,6 @@ namespace l::hid { - class INoteProcessor { - public: - virtual ~INoteProcessor() = default; - virtual void NoteOn(int32_t, int32_t = 127) {} - virtual void NoteOff() {} - virtual void NoteOff(int32_t) {} - virtual void NoteSustain(bool) {} - }; - enum class KeyFunctionTypes { OCTAVE_DOWN = 0, OCTAVE_UP @@ -31,7 +23,7 @@ namespace l::hid { const std::array charMapFromNoteBUpper = { '1', 'q', '2', 'w', '3', 'e', 'r', '5', 't', '6', 'y', '7', 'u', 'i', '9', 'o', '0', 'p' }; KeyboardPiano() : KeyboardPiano(nullptr, nullptr) {} - KeyboardPiano(l::hid::KeyState* keyState, l::hid::INoteProcessor* instrument) : + KeyboardPiano(l::hid::KeyState* keyState, l::audio::INoteProcessor* instrument) : mKeyState(keyState), mNotePlayer(instrument) { @@ -55,7 +47,7 @@ namespace l::hid { } void SetKeyState(KeyState* keyState); - void SetNoteProcessor(INoteProcessor* notePlayer); + void SetNoteProcessor(l::audio::INoteProcessor* notePlayer); void ForEachNoteChange(std::function noteHandler); void Update(); @@ -71,7 +63,7 @@ namespace l::hid { std::unordered_map mCharCodeToNote; std::unordered_map mKeyFunctions; KeyState* mKeyState = nullptr; - INoteProcessor* mNotePlayer = nullptr; + l::audio::INoteProcessor* mNotePlayer = nullptr; }; } diff --git a/packages/hid/source/common/KeyboardPiano.cpp b/packages/hid/source/common/KeyboardPiano.cpp index c6e2dcfb..84075d44 100644 --- a/packages/hid/source/common/KeyboardPiano.cpp +++ b/packages/hid/source/common/KeyboardPiano.cpp @@ -10,7 +10,7 @@ namespace l::hid { mKeyState = keyState; } - void KeyboardPiano::SetNoteProcessor(INoteProcessor* notePlayer) { + void KeyboardPiano::SetNoteProcessor(l::audio::INoteProcessor* notePlayer) { mNotePlayer = notePlayer; } diff --git a/packages/nodegraph/include/nodegraph/NodeGraphSchema.h b/packages/nodegraph/include/nodegraph/NodeGraphSchema.h index 4eeb98aa..d58dc5e3 100644 --- a/packages/nodegraph/include/nodegraph/NodeGraphSchema.h +++ b/packages/nodegraph/include/nodegraph/NodeGraphSchema.h @@ -12,8 +12,6 @@ #include "nodegraph/operations/NodeGraphOpSource.h" #include "nodegraph/operations/NodeGraphOpSignal.h" -#include "hid/Midi.h" - #include #include #include @@ -100,11 +98,6 @@ namespace l::nodegraph { bool RemoveNode(int32_t nodeId); NodeGraphBase* GetNode(int32_t nodeId); - template>> - NodeGraph* GetTypedNode(int32_t id) { - return mMainNodeGraph.GetTypedNode(id); - } - void ForEachNodeType(std::function&)> cb) const; void RegisterNodeType(const std::string& typeGroup, int32_t uniqueTypeId, std::string_view typeName); void ProcessSubGraph(int32_t numSamples); diff --git a/packages/nodegraph/include/nodegraph/core/NodeGraphBase.h b/packages/nodegraph/include/nodegraph/core/NodeGraphBase.h index 46564442..c1b0cb94 100644 --- a/packages/nodegraph/include/nodegraph/core/NodeGraphBase.h +++ b/packages/nodegraph/include/nodegraph/core/NodeGraphBase.h @@ -45,13 +45,8 @@ namespace l::nodegraph { virtual void ProcessSubGraph(int32_t numSamples = 1, bool recomputeSubGraphCache = true); virtual void Tick(int32_t tickCount, float elapsed); - virtual void SetNumInputs(int8_t numInputs); - virtual void SetNumOutputs(int8_t outputCount); - virtual void SetNumConstants(int8_t numConstants); - virtual int8_t GetNumInputs(); virtual int8_t GetNumOutputs(); - virtual int8_t GetNumConstants(); virtual float& GetOutput(int8_t outputChannel, int32_t size = 1); virtual float GetInput(int8_t inputChannel); @@ -69,13 +64,14 @@ namespace l::nodegraph { virtual bool SetInput(int8_t inputChannel, NodeGraphBase& source, int8_t sourceOutputChannel); virtual bool SetInput(int8_t inputChannel, NodeGraphGroup& source, int8_t sourceOutputChannel); - virtual bool SetInput(int8_t inputChannel, float constant, int32_t size = -1); + virtual bool SetInput(int8_t inputChannel, float constant, int32_t size = 1); virtual bool SetInput(int8_t inputChannel, float* floatPtr); - virtual void SetDefaultOutput(int8_t outputChannel, float constant, int32_t size = -1); + virtual void SetDefaultOutput(int8_t outputChannel, float constant, int32_t size = 1); virtual bool SetInputBound(int8_t inputChannel, InputBound bound = InputBound::INPUT_DONTCHANGE, float boundMin = 0.0f, float boundMax = 0.0f); - virtual bool RemoveInput(void* source); + virtual bool DetachInput(void* source); + virtual bool IsDataConstant(int8_t num); virtual bool IsDataVisible(int8_t num); virtual bool IsDataEditable(int8_t num); virtual bool IsOutputPolled(int8_t outputChannel); @@ -83,6 +79,9 @@ namespace l::nodegraph { virtual OutputType GetOutputType(); protected: + virtual void SetNumInputs(int8_t numInputs); + virtual void SetNumOutputs(int8_t outputCount); + virtual void ProcessOperation(int32_t numSamples = 1); bool mProcessUpdateHasRun = false; @@ -94,47 +93,47 @@ namespace l::nodegraph { OutputType mOutputType; std::string mName; - int8_t mInputCount = 0; - int8_t mConstantCount = 0; }; class NodeGraphOp { public: - NodeGraphOp(NodeGraphBase* node, int32_t numInputs = 1, int32_t numOutputs = 1, int32_t numConstants = 0) : + NodeGraphOp(NodeGraphBase* node, std::string_view name) : mNode(node), - mNumInputs(static_cast(numInputs)), - mNumOutputs(static_cast(numOutputs)), - mNumConstants(static_cast(numConstants)) + mName(name) {} virtual ~NodeGraphOp() { LOG(LogInfo) << "Node operation destroyed"; } - std::string defaultInStrings[4] = { "In 1", "In 2", "In 3", "In 4" }; - std::string defaultOutStrings[4] = { "Out 1", "Out 2", "Out 3", "Out 4" }; - - virtual void Reset() {} + virtual void Reset(); virtual void Process(int32_t, std::vector&, std::vector&) {}; virtual void Tick(int32_t, float) {} - virtual void SetNumInputs(int8_t numInputs); - virtual void SetNumOutputs(int8_t numOutputs); - virtual void SetNumConstants(int8_t numConstants); int8_t GetNumInputs(); int8_t GetNumOutputs(); - int8_t GetNumConstants(); - virtual bool IsDataVisible(int8_t num); - virtual bool IsDataEditable(int8_t num); + virtual bool IsDataConstant(int8_t channel); + virtual bool IsDataVisible(int8_t channel); + virtual bool IsDataEditable(int8_t channel); virtual std::string_view GetInputName(int8_t inputChannel); virtual std::string_view GetOutputName(int8_t outputChannel); virtual std::string_view GetName(); protected: + virtual void AddInput(std::string_view name, float defaultValue = 0.0f, int32_t size = 1, float boundMin = -l::math::constants::FLTMAX, float boundMax = l::math::constants::FLTMAX, bool visible = true, bool editable = true); + virtual void AddOutput(std::string_view name, float defaultValue = 0.0f, int32_t size = 1); + virtual void AddConstant(std::string_view name, float defaultValue = 0.0f, float boundMin = -l::math::constants::FLTMAX, float boundMax = l::math::constants::FLTMAX, bool visible = true, bool editable = true); + NodeGraphBase* mNode; + std::string mName; + + std::vector mDefaultInStrings; + std::vector mDefaultOutStrings; + std::vector> mDefaultInData; + std::vector> mDefaultOutData; + int8_t mNumInputs = 0; int8_t mNumOutputs = 0; - int8_t mNumConstants = 0; }; template @@ -146,28 +145,15 @@ namespace l::nodegraph { { SetNumInputs(mOperation.GetNumInputs()); SetNumOutputs(mOperation.GetNumOutputs()); - SetNumConstants(mOperation.GetNumConstants()); + + Reset(); } virtual ~NodeGraph() { LOG(LogInfo) << "Node destroyed"; } - virtual void SetNumInputs(int8_t numInputs) { - NodeGraphBase::SetNumInputs(numInputs); - mOperation.SetNumInputs(numInputs); - } - - virtual void SetNumOutputs(int8_t numOutputs) { - NodeGraphBase::SetNumOutputs(numOutputs); - mOperation.SetNumOutputs(numOutputs); - } - - virtual void SetNumConstants(int8_t numConstants) { - NodeGraphBase::SetNumConstants(numConstants); - mOperation.SetNumConstants(numConstants); - for (int8_t i = mInputCount; i < mInputCount + mConstantCount; i++) { - SetInput(i, 0.0f); - } + virtual bool IsDataConstant(int8_t num) override { + return mOperation.IsDataConstant(num); } virtual bool IsDataVisible(int8_t num) override { diff --git a/packages/nodegraph/include/nodegraph/core/NodeGraphGroup.h b/packages/nodegraph/include/nodegraph/core/NodeGraphGroup.h index 51d5955b..47f5af57 100644 --- a/packages/nodegraph/include/nodegraph/core/NodeGraphGroup.h +++ b/packages/nodegraph/include/nodegraph/core/NodeGraphGroup.h @@ -17,9 +17,16 @@ namespace l::nodegraph { class GraphDataCopy : public NodeGraphOp { public: - GraphDataCopy(NodeGraphBase* node) : - NodeGraphOp(node, 0) - {} + GraphDataCopy(NodeGraphBase* node, int32_t numInputsOutputs) : + NodeGraphOp(node, "Copy") + { + for (int32_t i = 0; i < numInputsOutputs; i++) { + AddInput("In " + std::to_string(i), 0.0f, 1); + } + for (int32_t i = 0; i < numInputsOutputs; i++) { + AddOutput("Out " + std::to_string(i), 0.0f, 1); + } + } virtual ~GraphDataCopy() = default; void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; @@ -27,14 +34,7 @@ namespace l::nodegraph { class NodeGraphGroup { public: - NodeGraphGroup() : - mInputNode(OutputType::Default), - mOutputNode(OutputType::Default) - { - SetNumInputs(1); - SetNumOutputs(1); - mOutputNodes.push_back(&mOutputNode); - } + NodeGraphGroup() {} ~NodeGraphGroup() { LOG(LogInfo) << "Node group destroyed"; } @@ -56,15 +56,9 @@ namespace l::nodegraph { bool ContainsNode(int32_t id); NodeGraphBase* GetNode(int32_t id); - template>> - NodeGraph* GetTypedNode(int32_t id) { - auto p = GetNode(id); - return reinterpret_cast*>(p); - } - bool RemoveNode(int32_t id); - template>, class... Params> + template, int> = 0> l::nodegraph::NodeGraphBase* NewNode(OutputType nodeType, Params&&... params) { mNodes.push_back(std::make_unique>(nodeType, std::forward(params)...)); auto nodePtr = mNodes.back().get(); @@ -78,8 +72,8 @@ namespace l::nodegraph { void ProcessSubGraph(int32_t numSamples, bool recomputeSubGraphCache = true); void Tick(int32_t tickCount, float elapsed); protected: - NodeGraph mInputNode; - NodeGraph mOutputNode; + NodeGraphBase* mInputNode = nullptr; + NodeGraphBase* mOutputNode = nullptr; std::vector> mNodes; std::vector mOutputNodes; diff --git a/packages/nodegraph/include/nodegraph/core/NodeGraphInput.h b/packages/nodegraph/include/nodegraph/core/NodeGraphInput.h index 313185f3..213365ef 100644 --- a/packages/nodegraph/include/nodegraph/core/NodeGraphInput.h +++ b/packages/nodegraph/include/nodegraph/core/NodeGraphInput.h @@ -22,7 +22,7 @@ namespace l::nodegraph { }; enum class InputBound { - INPUT_DONTCHANGE, + INPUT_DONTCHANGE = 0, INPUT_UNBOUNDED, INPUT_0_TO_1, INPUT_0_TO_2, @@ -31,6 +31,8 @@ namespace l::nodegraph { INPUT_CUSTOM, }; + std::pair GetInputBounds(InputBound bound); + class NodeGraphBase; union Input { @@ -47,7 +49,6 @@ namespace l::nodegraph { float mBoundMin = -l::math::constants::FLTMAX; float mBoundMax = l::math::constants::FLTMAX; - InputBound mInputBound = InputBound::INPUT_UNBOUNDED; int8_t mInputFromOutputChannel = 0; std::unique_ptr mName; @@ -59,6 +60,7 @@ namespace l::nodegraph { bool HasInputNode(); float Get(); float& Get(int32_t numSamples); + int32_t GetSize(); }; } diff --git a/packages/nodegraph/include/nodegraph/core/NodeGraphOutput.h b/packages/nodegraph/include/nodegraph/core/NodeGraphOutput.h index 9e524481..83a2f8ac 100644 --- a/packages/nodegraph/include/nodegraph/core/NodeGraphOutput.h +++ b/packages/nodegraph/include/nodegraph/core/NodeGraphOutput.h @@ -28,10 +28,10 @@ namespace l::nodegraph { std::unique_ptr mName = nullptr; bool mOutputPolled = false; - float& GetOutput(int32_t size = 1); - int32_t GetOutputSize(); - bool IsOutputPolled(); - void ResetOutputPollState(); + float& Get(int32_t size = 1); + int32_t GetSize(); + bool IsPolled(); + void ResetPollState(); }; } diff --git a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpEffect.h b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpEffect.h index 51e35055..b25b592b 100644 --- a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpEffect.h +++ b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpEffect.h @@ -25,9 +25,6 @@ namespace l::nodegraph { /*********************************************************************/ class GraphEffectReverb1 : public NodeGraphOp { public: - std::string defaultInStrings[11] = { "In 1", "In 2", "Mix", "Attenuation", "Room Size", "Delay 1", "Feedback 1", "Delay 2", "Feedback 2", "Delay 3", "Feedback 3" }; - std::string defaultOutStrings[2] = { "Out 1", "Out 2" }; - uint32_t GetFramesPerRoomSize(float roomSize) { const float metersPerFrame = 334.0f / 44100.0f; // (m/s)/(frames/s) = m/frames; const float metersToWallPerFrame = metersPerFrame / 2.0f; // half the distance to wall for the bounced distance @@ -38,32 +35,30 @@ namespace l::nodegraph { const float maxRoomSizeInMeters = 334.0f; // 334 meters large is 2 seconds of reverbation GraphEffectReverb1(NodeGraphBase * node) : - NodeGraphOp(node, 11, 2, 0) { - uint32_t bufferSize = GetFramesPerRoomSize(maxRoomSizeInMeters); - buf0.resize(bufferSize); - buf1.resize(bufferSize); - } + NodeGraphOp(node, "Reverb 1") { + + uint32_t bufferSize = GetFramesPerRoomSize(maxRoomSizeInMeters); + buf0.resize(bufferSize); + buf1.resize(bufferSize); + + AddInput("In 1", 0.0f); + AddInput("In 2", 0.0f); + AddInput("Mix", 0.75f, 1, 0.0f, 1.0f); + AddInput("Attenuation", 0.5f, 1, 0.0f, 1.0f); + AddInput("Room Size", 30.0f, 1, 0.2f, maxRoomSizeInMeters); + AddInput("Delay 1", 0.5f, 1, 0.0f, 1.0f); + AddInput("Feedback 1", 0.9f, 1, 0.0f, 1.0f); + AddInput("Delay 2", 0.8f, 1, 0.0f, 1.0f); + AddInput("Feedback 2", 0.9f, 1, 0.0f, 1.0f); + AddInput("Delay 3", 0.7f, 1, 0.0f, 1.0f); + AddInput("Feedback 3", 0.9f, 1, 0.0f, 1.0f); + + AddOutput("Out 1"); + AddOutput("Out 2"); + } virtual ~GraphEffectReverb1() = 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 "Reverb 1"; - } - protected: bool mInited = false; std::vector buf0; @@ -77,15 +72,11 @@ namespace l::nodegraph { float d0 = 0.0f; float d1 = 0.0f; float d2 = 0.0f; - }; /*********************************************************************/ class GraphEffectReverb2 : public NodeGraphOp { public: - std::string defaultInStrings[12] = { "In 1", "In 2", "Mix", "Feedback", "Room Size", "Width", "First tap", "Longest tap", "Num taps", "Tap bulge", "Filter cutoff", "Filter res"}; - std::string defaultOutStrings[4] = { "Rev 1", "Rev 2", "Tap 1", "Tap 2"}; - uint32_t GetFramesPerRoomSize(float roomSize) { const float metersPerFrame = 334.0f / 44100.0f; // (m/s)/(frames/s) = m/frames; const float metersToWallPerFrame = metersPerFrame / 2.0f; // half the distance to wall for the bounced distance @@ -96,34 +87,35 @@ namespace l::nodegraph { const float maxRoomSizeInMeters = 334.0f; // 334 meters large is 2 seconds of reverbation GraphEffectReverb2(NodeGraphBase* node) : - NodeGraphOp(node, 12, 4, 0) + NodeGraphOp(node, "Reverb 2") { uint32_t bufferSize = GetFramesPerRoomSize(maxRoomSizeInMeters); bufRev0.resize(bufferSize); bufRev1.resize(bufferSize); bufEarlyTap0.resize(bufferSize); bufEarlyTap1.resize(bufferSize); + + AddInput("In 1"); + AddInput("In 2"); + AddInput("Mix", 0.3f, 1, 0.0f, 1.0f); + AddInput("Feedback", 0.5f, 1, 0.0f, 1.0f); + AddInput("Room Size", 30.0f, 1, 0.2f, maxRoomSizeInMeters); + AddInput("Width", 0.5f, 1, 0.0f, 1.0f); + AddInput("First tap", 0.1f, 1, 0.0f, 10.0f); + AddInput("Longest tap", 0.8f, 1, 0.0f, 10.0f); + AddInput("Num tapss", 5.0f, 1, 1.0f, 30.0f); + AddInput("Tap bulge", 0.7f, 1, 1.0f, 10.0f); + AddInput("Filter cutoff", 0.95f, 1, 0.001f, 0.999f); + AddInput("Filter res", 0.01f, 1, 0.001f, 0.999f); + + AddOutput("Rev 1"); + AddOutput("Rev 2"); + AddOutput("Tap 1"); + AddOutput("Tap 2"); } virtual ~GraphEffectReverb2() = 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 "Reverb 2"; - } - protected: bool mInited = false; @@ -145,32 +137,25 @@ namespace l::nodegraph { /*********************************************************************/ 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) - {} + NodeGraphOp(node, "Limiter") + { + AddInput("In 1"); + AddInput("In 2"); + AddInput("Attack", 5.0f, 1, 1.0f, 10000.0f); + AddInput("Release", 100.0f, 1, 1.0f, 10000.0f); + AddInput("Preamp", 1.0f, 1, 0.0f, 10.0f); + AddInput("Limit", 0.95f, 1, 0.0f, 10.0f); - virtual ~GraphEffectLimiter() = 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 "Limiter"; + AddOutput("Out 1"); + AddOutput("Out 2"); + AddOutput("Envelope"); + + mEnvelope = 0.0f; } + virtual ~GraphEffectLimiter() = default; + virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; protected: float mEnvelope = 0.0f; }; @@ -178,32 +163,24 @@ namespace l::nodegraph { /*********************************************************************/ class GraphEffectEnvelope : public NodeGraphOp { public: - std::string defaultInStrings[5] = { "Freq", "Velocity", "Attack", "Release", "Fade" }; - std::string defaultOutStrings[2] = { "Freq", "Volume" }; - GraphEffectEnvelope(NodeGraphBase* node) : - NodeGraphOp(node, 5, 2) - {} + NodeGraphOp(node, "Envelope") + { + AddInput("Freq"); + AddInput("Velocity", 0.5f, 1, 0.0f, 1.0f); + AddInput("Attack", 50.0f, 1, 1.0f, 10000.0f); + AddInput("Release", 50.0f, 1, 1.0f, 10000.0f); + AddInput("Fade", 0.1f, 1, 0.0001f, 1.0f); + + AddOutput("Freq"); + AddOutput("Volume"); + + mEnvelope = 0.0f; + } virtual ~GraphEffectEnvelope() = default; - virtual void Reset() override; virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; virtual void Tick(int32_t, float) {} - 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 bool IsDataVisible(int8_t channel) override { - return channel >= 1 ? true : false; - } - virtual bool IsDataEditable(int8_t channel) override { - return channel >= 1 ? true : false; - } - virtual std::string_view GetName() override { - return "Envelope"; - } protected: int32_t mFrameCount = 0; float mEnvelopeTarget = 0.0f; @@ -214,32 +191,21 @@ namespace l::nodegraph { /*********************************************************************/ 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) - {} + NodeGraphOp(node, "Envelope Follower") + { + AddInput("In 1"); + AddInput("In 2"); + AddInput("Attack", 5.0f, 1, 1.0f, 10000.0f); + AddInput("Release", 100.0f, 1, 1.0f, 10000.0f); - virtual ~GraphEffectEnvelopeFollower() = 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 "Envelope Follower"; + AddOutput("Envelope"); + + mEnvelope = 0.0f; } + virtual ~GraphEffectEnvelopeFollower() = default; + virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; protected: float mEnvelope = 0.0f; }; @@ -247,32 +213,24 @@ namespace l::nodegraph { /*********************************************************************/ 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) - {} + NodeGraphOp(node, "Saturator") + { + AddInput("In 1"); + AddInput("In 2"); + AddInput("Wet", 0.5f, 1, 0.0f, 1.0f); + AddInput("Preamp", 1.5f, 1, 0.0f, 10.0f); + AddInput("Limit", 0.6f, 1, 0.0f, 10.0f); + AddInput("Postamp", 1.4f, 1, 0.0f, 10.0f); - virtual ~GraphEffectSaturator() = 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 "Saturator"; + AddOutput("Out 1"); + AddOutput("Out 2"); + + mEnvelope = 0.0f; } + virtual ~GraphEffectSaturator() = default; + virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; protected: float mEnvelope = 0.0f; }; @@ -280,9 +238,6 @@ namespace l::nodegraph { /*********************************************************************/ class GraphEffectTranceGate : public NodeGraphOp { public: - std::string defaultInStrings[7] = { "In 1", "In 2", "Bpm", "Fmod", "Attack", "Pattern", "Drift Sync"}; - 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}, @@ -297,28 +252,24 @@ namespace l::nodegraph { }; GraphEffectTranceGate(NodeGraphBase* node) : - NodeGraphOp(node, 7, 2, 0) - {} + NodeGraphOp(node, "Trance Gate") + { + AddInput("In 1"); + AddInput("In 2"); + AddInput("Bpm", 60.0f, 1, 1.0f, 1000.0f); + AddInput("Fmod", 1.0f, 1, 0.01f, 1.0f); + AddInput("Attack", 0.001f, 1, 0.0f, 1.0f); + AddInput("Pattern", 0.0f, 1, 0.0f, 100.0f); + AddInput("Sync", 0.0f, 1, 0.0f, 1.0f); - 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"; + AddOutput("Out 1"); + AddOutput("Out 2"); + + mGateIndex = 0; } + virtual ~GraphEffectTranceGate() = default; + virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; protected: float mSamplesUntilUpdate = 0.0f; int32_t mGateIndex = 0; @@ -331,32 +282,28 @@ namespace l::nodegraph { /*********************************************************************/ class GraphEffectArpeggio: public NodeGraphOp { public: - std::string defaultInStrings[8] = { "Note On Id", "Note Off Id", "Velocity", "Bpm", "Fmod", "Attack", "Smooth", "Drift Sync"}; - std::string defaultOutStrings[3] = { "Freq", "Volume" }; + const static int32_t gPolyphony = 12; GraphEffectArpeggio(NodeGraphBase* node) : - NodeGraphOp(node, 8, 2, 0) - {} + NodeGraphOp(node, "Arpeggio") + { + AddInput("Note On", l::audio::gNoNote_f, gPolyphony, -499.0, 500.0); + AddInput("Note Off", l::audio::gNoNote_f, gPolyphony, -499.0, 500.0); + AddInput("Velocity", 1.0f, 1, 0.0f, 1.0f); + AddInput("Bpm", 60.0f, 1, 1.0f, 1000.0f); + AddInput("Fmod", 1.0f, 1, 0.01f, 1.0f); + AddInput("Attack", 0.01f, 1, 0.0f, 1.0f); + AddInput("Smooth", 0.0f, 1, 0.0f, 1.0f); + AddInput("Sync", 0.0f, 1, 0.0f, 1.0f); - virtual ~GraphEffectArpeggio() = default; - virtual void Reset() override; - virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; - virtual bool IsDataVisible(int8_t) override { - return true; - } - virtual bool IsDataEditable(int8_t channel) override { - return channel > 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 "Arpeggio"; + AddOutput("Freq"); + AddOutput("Volume"); + + mGainTarget = 0.0f; } + virtual ~GraphEffectArpeggio() = default; + virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; protected: float mSamplesUntilUpdate = 0.0f; float mGainTarget = 0.0f; diff --git a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpFilter.h b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpFilter.h index 7081bfd2..c7b103d7 100644 --- a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpFilter.h +++ b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpFilter.h @@ -29,40 +29,29 @@ namespace l::nodegraph { static const int8_t mNumDefaultInputs = 4; static const int8_t mNumDefaultOutputs = 1; - GraphFilterBase(NodeGraphBase* node, std::string_view name, int32_t numInputs = 0, int32_t numOutputs = 0, int32_t numConstants = 0) : - NodeGraphOp(node, mNumDefaultInputs + numInputs, mNumDefaultOutputs + numOutputs, numConstants), - mName(name) - {} - - std::string defaultInStrings[mNumDefaultInputs] = { "Sync", "In", "Cutoff", "Resonance" }; - std::string defaultOutStrings[mNumDefaultOutputs] = { "Out" }; + GraphFilterBase(NodeGraphBase* node, std::string_view name) : + NodeGraphOp(node, name) + { + mDefaultInStrings.clear(); + mDefaultOutStrings.clear(); + mDefaultInData.clear(); + + AddInput("Sync", 0.0f); + AddInput("In"); + AddInput("Cutoff", 0.0f); + AddInput("Resonance", 0.0f); + AddOutput("Out", 0.0f); + } virtual ~GraphFilterBase() = default; virtual void Reset() override final; virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override final; - virtual bool IsDataVisible(int8_t) override { return true; } - virtual bool IsDataEditable(int8_t) override { return true; } - virtual std::string_view GetInputName(int8_t inputChannel) override final { - if (inputChannel >= mNumDefaultInputs) return GetInputNameExtra(inputChannel - mNumDefaultInputs); - if (inputChannel >= 0) return defaultInStrings[static_cast(inputChannel)]; - return ""; - } - virtual std::string_view GetOutputName(int8_t outputChannel) override final { - if (outputChannel >= mNumDefaultOutputs) return GetOutputNameExtra(outputChannel - mNumDefaultOutputs); - return defaultOutStrings[outputChannel]; - } - virtual std::string_view GetName() override { - return mName; - } - virtual std::string_view GetInputNameExtra(int8_t) { return ""; }; - virtual std::string_view GetOutputNameExtra(int8_t) { return ""; }; virtual void ResetInput() {}; virtual void ResetSignal() {}; virtual void UpdateSignal(std::vector&, std::vector&) {}; virtual float ProcessSignal(float input, float cutoff, float resonance) = 0; protected: - std::string mName; float mReset = 0.0f; float mSamplesUntilUpdate = 0.0f; float mUpdateSamples = 16.0f; @@ -97,7 +86,6 @@ namespace l::nodegraph { GraphFilterHighpass(NodeGraphBase* node) : GraphFilterBase(node, "Highpass") {} - virtual ~GraphFilterHighpass() = default; virtual void ResetInput() override; @@ -114,21 +102,17 @@ namespace l::nodegraph { class GraphFilterChamberlain2pole : public GraphFilterBase { public: GraphFilterChamberlain2pole(NodeGraphBase* node) : - GraphFilterBase(node, "Chamberlin two-pole", 1) + GraphFilterBase(node, "Chamberlin two-pole") { + AddInput("Mode"); mState.resize(4); } - virtual ~GraphFilterChamberlain2pole() = default; virtual void ResetInput() override; virtual void ResetSignal() override; virtual void UpdateSignal(std::vector&, std::vector&) override; virtual float ProcessSignal(float input, float cutoff, float resonance) override; - - virtual std::string_view GetInputNameExtra(int8_t) override { - return "Mode"; - } protected: float mInputValuePrev = 0.0f; float mScale = 0.0f; diff --git a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpInput.h b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpInput.h index dd497a9d..be1212b3 100644 --- a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpInput.h +++ b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpInput.h @@ -4,6 +4,7 @@ #include "logging/LoggingAll.h" #include "hid/KeyboardPiano.h" +#include "hid/KeyState.h" #include "hid/Midi.h" #include "audio/PortAudio.h" @@ -23,31 +24,27 @@ namespace l::nodegraph { /*********************************************************************/ - class GraphInputKeyboardPiano : public NodeGraphOp, public l::hid::INoteProcessor { + class GraphInputKeyboardPiano : public NodeGraphOp, public l::audio::INoteProcessor { public: GraphInputKeyboardPiano(NodeGraphBase* node, l::hid::KeyState* keyState) : - NodeGraphOp(node, 0, 3, 3) + NodeGraphOp(node, "Keyboard") { + AddOutput("Freq", 0.0f); + AddOutput("Note On", l::audio::gNoNote_f); + AddOutput("Note Off", l::audio::gNoNote_f); + + AddConstant("Freq", 0.0f); + AddConstant("Note On", l::audio::gNoNote_f); + AddConstant("Note Off", l::audio::gNoNote_f); + mChannel.resize(1); mKeyboard.SetKeyState(keyState); mKeyboard.SetNoteProcessor(this); } - std::string defaultOutStrings[3] = { "Freq", "Note On Id", "Note Off Id" }; - virtual ~GraphInputKeyboardPiano() = default; virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; virtual void Tick(int32_t tickCount, float elapsed) override; - virtual void Reset() override; - virtual std::string_view GetOutputName(int8_t outputChannel) override { - return defaultOutStrings[outputChannel]; - } - virtual std::string_view GetName() override { - return "Keyboard"; - } - virtual bool IsDataVisible(int8_t) override { - return true; - } virtual void NoteOn(int32_t note, int32_t velocity) override; virtual void NoteOff() override; virtual void NoteOff(int32_t note) override; @@ -63,12 +60,26 @@ namespace l::nodegraph { }; /*********************************************************************/ - class GraphInputMidiKeyboard : public NodeGraphOp, public l::hid::INoteProcessor { + class GraphInputMidiKeyboard : public NodeGraphOp, public l::audio::INoteProcessor { public: + const static int32_t gPolyphony = 12; + GraphInputMidiKeyboard(NodeGraphBase* node, l::hid::midi::MidiManager* midiManager) : - NodeGraphOp(node, 0, 5, 5), + NodeGraphOp(node, ""), mMidiManager(midiManager) { + AddOutput("Freq", 0.0f); + AddOutput("Velocity", 0.0f); + AddOutput("Note On", l::audio::gNoNote_f, gPolyphony); + AddOutput("Note Off", l::audio::gNoNote_f, gPolyphony); + AddOutput("Device Id", 0.0f); + + AddConstant("Freq", 0.0f); + AddConstant("Velocity", 0.0f); + AddConstant("Note On", l::audio::gNoNote_f, gPolyphony); + AddConstant("Note Off", l::audio::gNoNote_f, gPolyphony); + AddConstant("Device Id", 0.0f, 0.0f, 10.0f); + mChannel.resize(1); SetDevice(0); @@ -80,24 +91,9 @@ namespace l::nodegraph { }); } - std::string defaultOutStrings[5] = { "Freq", "Velocity", "Note On Id", "Note Off Id", "Device Id"}; - virtual ~GraphInputMidiKeyboard() = default; - virtual void Reset() override; virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; virtual void Tick(int32_t tick, float deltaTime) override; - virtual std::string_view GetOutputName(int8_t outputChannel) override { - return defaultOutStrings[outputChannel]; - } - virtual std::string_view GetName() override { - return mNodeName; - } - virtual bool IsDataVisible(int8_t) override { - return true; - } - virtual bool IsDataEditable(int8_t channel) override { - return channel >= 4 ? true : false; - } virtual void MidiEvent(const l::hid::midi::MidiData& data); virtual void NoteOn(int32_t note, int32_t velocity = 127) override; @@ -114,8 +110,8 @@ namespace l::nodegraph { if (deviceInfo) { mMidiDeviceInId = deviceId; mMidiChannelKeys = deviceInfo->mChannelKeys; - mNodeName = deviceInfo->GetName(); - mNodeName += " : Midi"; + mName = deviceInfo->GetName(); + mName += " : Midi"; } } } @@ -128,7 +124,6 @@ namespace l::nodegraph { std::vector> mChannel; int32_t mMidiDeviceInId = -1; int32_t mMidiChannelKeys = -1; - std::string mNodeName; std::vector mSustainedNotes; }; @@ -136,9 +131,18 @@ namespace l::nodegraph { class GraphInputMidiKnobs : public NodeGraphOp { public: GraphInputMidiKnobs(NodeGraphBase* node, l::hid::midi::MidiManager* midiManager) : - NodeGraphOp(node, 0, 9, 9), + NodeGraphOp(node, ""), mMidiManager(midiManager) { + for (int32_t i = 0; i < 8; i++) { + AddOutput("Knob " + std::to_string(i), 0.0f); + } + for (int32_t i = 0; i < 8; i++) { + AddConstant("Knob " + std::to_string(i), 0.0f); + } + AddOutput("Device Id", 0.0f); + AddConstant("Device Id", 0.0f, 0.0f, 10.0f); + SetDevice(0); mMidiManager->RegisterCallback([&](l::hid::midi::MidiData data) { @@ -148,25 +152,9 @@ namespace l::nodegraph { }); } - std::string defaultOutStrings[9] = { "Knob 1", "Knob 2", "Knob 3", "Knob 4", "Knob 5", "Knob 6", "Knob 7", "Knob 8", "Device Id"}; - virtual ~GraphInputMidiKnobs() = default; - virtual void Reset() override; virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; virtual void Tick(int32_t tick, float deltaTime) override; - virtual std::string_view GetOutputName(int8_t outputChannel) override { - return defaultOutStrings[outputChannel]; - } - virtual std::string_view GetName() override { - return mNodeName; - } - virtual bool IsDataVisible(int8_t) override { - return true; - } - virtual bool IsDataEditable(int8_t channel) override { - return channel >= 8 ? true : false; - } - virtual void MidiEvent(const l::hid::midi::MidiData& data); protected: void SetDevice(int32_t deviceId) { @@ -175,8 +163,8 @@ namespace l::nodegraph { if (deviceInfo) { mMidiDeviceInId = deviceId; mMidiChannelKnobs = deviceInfo->mChannelKnobs; - mNodeName = deviceInfo->GetName(); - mNodeName += " : Knobs"; + mName = deviceInfo->GetName(); + mName += " : Knobs"; } } } @@ -184,7 +172,6 @@ namespace l::nodegraph { l::hid::midi::MidiManager* mMidiManager = nullptr; int32_t mMidiDeviceInId = -1; int32_t mMidiChannelKnobs = -1; - std::string mNodeName; }; /*********************************************************************/ @@ -221,16 +208,20 @@ namespace l::nodegraph { const std::vector remapToButtonStatesToColor = {0, 6, 3, 5, 1, 4, 2}; GraphInputMidiButtons(NodeGraphBase* node, l::hid::midi::MidiManager* midiManager, int32_t buttonGroup) : - NodeGraphOp(node, 0, 9, 9), + NodeGraphOp(node, ""), mMidiManager(midiManager), mButtonGroup(buttonGroup) { - mButtonStates.resize(8); - - for (int8_t i = 0; i < 8; i++) { - defaultOutStrings[i] = std::string("Pad ") + std::to_string(i); + for (int32_t i = 0; i < 8; i++) { + AddOutput("Pad " + std::to_string(i), 0.0f); + } + for (int32_t i = 0; i < 8; i++) { + AddConstant("Pad " + std::to_string(i), 0.0f); } - defaultOutStrings[8] = "Device Id"; + AddOutput("Device Id", 0.0f); + AddConstant("Device Id", 0.0f, 0.0f, 10.0f); + + mButtonStates.resize(8); SetDevice(0); @@ -260,21 +251,8 @@ namespace l::nodegraph { mMidiManager->UnregisterCallback(mCallbackId); } - virtual void Reset() override; virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; virtual void Tick(int32_t tick, float deltaTime) override; - virtual std::string_view GetOutputName(int8_t outputChannel) override { - return defaultOutStrings[outputChannel]; - } - virtual std::string_view GetName() override { - return mNodeName; - } - virtual bool IsDataVisible(int8_t) override { - return true; - } - virtual bool IsDataEditable(int8_t channel) override { - return channel >= 8 ? true : false; - } void UpdateButton(int8_t buttonId, int8_t buttonState) { if (mMidiManager) { @@ -304,9 +282,9 @@ namespace l::nodegraph { if (deviceInfo) { mMidiDeviceInId = deviceId; mMidiChannelButtons = deviceInfo->mChannelButtons; - mNodeName = deviceInfo->GetName(); - mNodeName += " : Pads"; - mNodeName += std::to_string(mButtonGroup); + mName = deviceInfo->GetName(); + mName += " : Pads"; + mName += std::to_string(mButtonGroup); } } } @@ -317,7 +295,6 @@ namespace l::nodegraph { int32_t mCallbackId = 0; int32_t mButtonGroup; - std::string mNodeName; bool mMidiShiftState = false; std::vector mButtonStates; int32_t mInitCounter = 0; diff --git a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpLogic.h b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpLogic.h index cee4967b..7bc7785b 100644 --- a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpLogic.h +++ b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpLogic.h @@ -28,8 +28,12 @@ namespace l::nodegraph { class GraphLogicalAnd : public NodeGraphOp { public: GraphLogicalAnd(NodeGraphBase* node) : - NodeGraphOp(node, 2, 1) - {} + NodeGraphOp(node, "And") + { + AddInput("In 1", 0.0f); + AddInput("In 2", 0.0f); + AddOutput("Out", 0.0f); + } virtual ~GraphLogicalAnd() = default; virtual void Process(int32_t, std::vector& inputs, std::vector& outputs) override { @@ -37,19 +41,18 @@ namespace l::nodegraph { bool input2 = inputs.at(1).Get() != 0.0f; outputs.at(0).mOutput = (input1 && input2) ? 1.0f : 0.0f; } - std::string_view GetName() override { - return "And"; - } - virtual bool IsDataVisible(int8_t) override { return true; } - virtual bool IsDataEditable(int8_t) override { return true; } }; /*********************************************************************/ class GraphLogicalOr : public NodeGraphOp { public: GraphLogicalOr(NodeGraphBase* node) : - NodeGraphOp(node, 2, 1) - {} + NodeGraphOp(node, "Or") + { + AddInput("In 1", 0.0f); + AddInput("In 2", 0.0f); + AddOutput("Out", 0.0f); + } virtual ~GraphLogicalOr() = default; virtual void Process(int32_t, std::vector& inputs, std::vector& outputs) override { @@ -57,19 +60,18 @@ namespace l::nodegraph { bool input2 = inputs.at(1).Get() != 0.0f; outputs.at(0).mOutput = (input1 || input2) ? 1.0f : 0.0f; } - std::string_view GetName() override { - return "Or"; - } - virtual bool IsDataVisible(int8_t) override { return true; } - virtual bool IsDataEditable(int8_t) override { return true; } }; /*********************************************************************/ class GraphLogicalXor : public NodeGraphOp { public: GraphLogicalXor(NodeGraphBase* node) : - NodeGraphOp(node, 2, 1) - {} + NodeGraphOp(node, "Xor") + { + AddInput("In 1", 0.0f); + AddInput("In 2", 0.0f); + AddOutput("Out", 0.0f); + } virtual ~GraphLogicalXor() = default; virtual void Process(int32_t, std::vector& inputs, std::vector& outputs) override { @@ -77,11 +79,6 @@ namespace l::nodegraph { bool input2 = inputs.at(1).Get() != 0.0f; outputs.at(0).mOutput = (input1 ^ input2) ? 1.0f : 0.0f; } - virtual std::string_view GetName() override { - return "Xor"; - } - virtual bool IsDataVisible(int8_t) override { return true; } - virtual bool IsDataEditable(int8_t) override { return true; } }; } diff --git a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpNumeric.h b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpNumeric.h index 67c63855..7388a1c9 100644 --- a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpNumeric.h +++ b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpNumeric.h @@ -26,93 +26,82 @@ namespace l::nodegraph { class GraphNumericAdd : public NodeGraphOp { public: GraphNumericAdd(NodeGraphBase* node) : - NodeGraphOp(node, 2, 1) - {} + NodeGraphOp(node, "Add") + { + AddInput("In 1"); + AddInput("In 2"); + AddOutput("Out"); + } virtual ~GraphNumericAdd() = default; virtual void Process(int32_t, std::vector& inputs, std::vector& outputs) override { outputs.at(0).mOutput = inputs.at(0).Get() + inputs.at(1).Get(); } - virtual std::string_view GetName() override { - return "Add"; - } - virtual bool IsDataVisible(int8_t) override { return true; } - virtual bool IsDataEditable(int8_t) override { return true; } }; /*********************************************************************/ class GraphNumericMultiply : public NodeGraphOp { public: GraphNumericMultiply(NodeGraphBase* node) : - NodeGraphOp(node, 2, 1) - {} + NodeGraphOp(node, "Multiply") + { + AddInput("In 1"); + AddInput("In 2"); + AddOutput("Out"); + } virtual ~GraphNumericMultiply() = default; virtual void Process(int32_t, std::vector& inputs, std::vector& outputs) override { outputs.at(0).mOutput = inputs.at(0).Get() * inputs.at(1).Get(); } - virtual std::string_view GetName() override { - return "Multiply"; - } - virtual bool IsDataVisible(int8_t) override { return true; } - virtual bool IsDataEditable(int8_t) override { return true; } }; /*********************************************************************/ class GraphNumericSubtract : public NodeGraphOp { public: GraphNumericSubtract(NodeGraphBase* node) : - NodeGraphOp(node, 2, 1) - {} + NodeGraphOp(node, "Subtract") + { + AddInput("In 1"); + AddInput("In 2"); + AddOutput("Out"); + } virtual ~GraphNumericSubtract() = default; virtual void Process(int32_t, std::vector& inputs, std::vector& outputs) override { outputs.at(0).mOutput = inputs.at(0).Get() - inputs.at(1).Get(); } - virtual std::string_view GetName() override { - return "Subtract"; - } - virtual bool IsDataVisible(int8_t) override { return true; } - virtual bool IsDataEditable(int8_t) override { return true; } }; /*********************************************************************/ class GraphNumericNegate : public NodeGraphOp { public: GraphNumericNegate(NodeGraphBase* node) : - NodeGraphOp(node, 1, 1) - {} + NodeGraphOp(node, "Negate") + { + AddInput("In"); + AddOutput("Out"); + } virtual ~GraphNumericNegate() = default; virtual void Process(int32_t, std::vector& inputs, std::vector& outputs) override { outputs.at(0).mOutput = -inputs.at(0).Get(); } - virtual std::string_view GetName() override { - return "Negate"; - } - virtual bool IsDataVisible(int8_t) override { return true; } - virtual bool IsDataEditable(int8_t) override { return true; } }; /*********************************************************************/ class GraphNumericIntegral : public NodeGraphOp { public: GraphNumericIntegral(NodeGraphBase* node) : - NodeGraphOp(node, 1, 1) - {} - - virtual ~GraphNumericIntegral() = default; - virtual void Reset() override { - mOutput = 0.0f; + NodeGraphOp(node, "Integral") + { + AddInput("In"); + AddOutput("Out"); } + virtual ~GraphNumericIntegral() = default; virtual void Process(int32_t, std::vector& inputs, std::vector& outputs) override { mOutput += inputs.at(0).Get(); outputs.at(0).mOutput = mOutput; } - virtual std::string_view GetName() override { - return "Integral"; - } - virtual bool IsDataVisible(int8_t) override { return true; } - virtual bool IsDataEditable(int8_t) override { return true; } protected: float mOutput = 0.0f; }; @@ -121,59 +110,51 @@ namespace l::nodegraph { class GraphNumericMultiply3 : public NodeGraphOp { public: GraphNumericMultiply3(NodeGraphBase* node) : - NodeGraphOp(node, 3, 1) - {} + NodeGraphOp(node, "Multiply3") + { + AddInput("In 1"); + AddInput("In 2"); + AddInput("In 3"); + AddOutput("Out"); + } virtual ~GraphNumericMultiply3() = default; virtual void Process(int32_t, std::vector& inputs, std::vector& outputs) override { outputs.at(0).mOutput = inputs.at(0).Get() * inputs.at(1).Get() * inputs.at(2).Get(); } - virtual std::string_view GetName() override { - return "Multiply3"; - } - virtual bool IsDataVisible(int8_t) override { return true; } - virtual bool IsDataEditable(int8_t) override { return true; } }; /*********************************************************************/ class GraphNumericMultiplyAndAdd : public NodeGraphOp { public: GraphNumericMultiplyAndAdd(NodeGraphBase* node) : - NodeGraphOp(node, 3, 1) - {} - - std::string defaultInStrings[3] = { "Factor 1", "Factor 2", "Term 1" }; + NodeGraphOp(node, "Multiply & Add") + { + AddInput("In 1"); + AddInput("In 2"); + AddInput("In 3"); + AddOutput("Out"); + } virtual ~GraphNumericMultiplyAndAdd() = default; void virtual Process(int32_t, std::vector& inputs, std::vector& outputs) override { outputs.at(0).mOutput = inputs.at(0).Get() * inputs.at(1).Get() + inputs.at(2).Get(); } - virtual std::string_view GetName() override { - return "Multiply & Add"; - } - - virtual std::string_view GetInputName(int8_t inputChannel) { - return defaultInStrings[inputChannel]; - } - virtual bool IsDataVisible(int8_t) override { return true; } - virtual bool IsDataEditable(int8_t) override { return true; } }; /*********************************************************************/ class GraphNumericRound : public NodeGraphOp { public: GraphNumericRound(NodeGraphBase* node) : - NodeGraphOp(node, 1, 1) - {} + NodeGraphOp(node, "Round") + { + AddInput("In"); + AddOutput("Out"); + } virtual ~GraphNumericRound() = default; virtual void Process(int32_t, std::vector& inputs, std::vector& outputs) override { outputs.at(0).mOutput = l::math::functions::round(inputs.at(0).Get()); } - virtual std::string_view GetName() override { - return "Round"; - } - virtual bool IsDataVisible(int8_t) override { return true; } - virtual bool IsDataEditable(int8_t) override { return true; } }; } diff --git a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpOutput.h b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpOutput.h index a37229c2..50032c8d 100644 --- a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpOutput.h +++ b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpOutput.h @@ -25,26 +25,18 @@ namespace l::nodegraph { /*********************************************************************/ class GraphOutputDebug : public NodeGraphOp { public: - std::string defaultInStrings[2] = { "Debug", "Smooth" }; GraphOutputDebug(NodeGraphBase* node) : - NodeGraphOp(node, 1, 0, 2) - {} + NodeGraphOp(node, "Debug") + { + AddInput("Debug"); + AddConstant("Smooth", 0.5f, 0.0f, 1.0f); + AddConstant(""); + + mValue = 0.0; + } virtual ~GraphOutputDebug() = default; - virtual void Reset() override; virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; - virtual bool IsDataVisible(int8_t) override { - return true; - } - virtual bool IsDataEditable(int8_t channel) override { - return channel == 1; - } - virtual std::string_view GetOutputName(int8_t outputChannel) override { - return defaultInStrings[outputChannel]; - } - virtual std::string_view GetName() override { - return "Debug"; - } protected: float mValue = 0.0F; }; @@ -52,56 +44,41 @@ namespace l::nodegraph { /*********************************************************************/ class GraphOutputSpeaker : public NodeGraphOp { public: - std::string defaultInStrings[3] = { "Left", "Right", "Volume"}; - GraphOutputSpeaker(NodeGraphBase* node, l::audio::AudioStream* stream = nullptr) : - NodeGraphOp(node, 3, 0, 0), + NodeGraphOp(node, "Speaker"), mAudioStream(stream), mCurrentStereoPosition(0) - {} - virtual ~GraphOutputSpeaker() = default; + { + AddInput("Left"); + AddInput("Right"); + AddInput("Volume", 0.5f, 1, 0.0f, 1.0f); - virtual void Reset() override; - virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; - virtual bool IsDataVisible(int8_t) override { - return true; - } - virtual bool IsDataEditable(int8_t channel) override { - return channel == 2 ? true : false; - } - virtual std::string_view GetInputName(int8_t outputChannel) override { - return defaultInStrings[outputChannel]; - } - virtual std::string_view GetName() override { - return "Speaker"; + mEnvelope = 0.0f; + mFilterEnvelope.SetConvergence(); } + virtual ~GraphOutputSpeaker() = default; + virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; protected: l::audio::AudioStream* mAudioStream; int32_t mCurrentStereoPosition; float mEnvelope = 0.0f; + l::audio::FilterRWA mFilterEnvelope; }; /*********************************************************************/ class GraphOutputPlot : public NodeGraphOp { public: - std::string defaultInStrings[2] = { "Plot", "Smooth" }; GraphOutputPlot(NodeGraphBase* node, int32_t plotSamples) : - NodeGraphOp(node, 1, 1, 0), + NodeGraphOp(node, "Plot"), mPlotSamples(plotSamples) { - mNode->GetOutput(0, mPlotSamples); + AddInput("Plot", 0.0f, 1, -1.0f, 1.0f); + AddOutput("Plot", 0.0f, mPlotSamples); } virtual ~GraphOutputPlot() = default; - virtual void Reset() override; virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; - virtual std::string_view GetOutputName(int8_t outputChannel) override { - return defaultInStrings[outputChannel]; - } - virtual std::string_view GetName() override { - return "Plot"; - } protected: int32_t mPlotSamples = 50; int32_t mCurIndex = 0; diff --git a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpSignal.h b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpSignal.h index 508191ce..7ee5ab84 100644 --- a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpSignal.h +++ b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpSignal.h @@ -28,40 +28,33 @@ namespace l::nodegraph { static const int8_t mNumDefaultInputs = 5; static const int8_t mNumDefaultOutputs = 1; - GraphSignalBase(NodeGraphBase* node, std::string_view name, int32_t numInputs = 0, int32_t numOutputs = 0, int32_t numConstants = 0) : - NodeGraphOp(node, mNumDefaultInputs + numInputs, mNumDefaultOutputs + numOutputs, numConstants), - mName(name) - {} - - std::string defaultInStrings[mNumDefaultInputs] = { "Sync", "Rate", "Freq", "Volume", "Smooth"}; - std::string defaultOutStrings[mNumDefaultOutputs] = { "Out" }; + GraphSignalBase(NodeGraphBase* node, std::string_view name) : + NodeGraphOp(node, name) + { + AddInput("Sync", 0.0f, 1, 0.0f, 1.0f); + AddInput("Rate", 256.0f, 1, 1.0f, 2048.0f); + AddInput("Freq", 0.0f, 1, 0.0f, 22050.0f); + AddInput("Volume", 0.5f, 1, 0.0f, 5.0f); + AddInput("Smooth", 1.0f, 1, 0.0f, 1.0f); + AddOutput("Out"); + + mFreq = 0.0f; + mVolumeTarget = 0.0f; + mSmooth = 0.5f; + mDeltaTime = 0.0f; + mSamplesUntilUpdate = 0.0f; + + ResetInput(); + } virtual ~GraphSignalBase() = default; - virtual void Reset() override final; virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override final; - virtual bool IsDataVisible(int8_t) override { return true; } - virtual bool IsDataEditable(int8_t) override { return true; } - virtual std::string_view GetInputName(int8_t inputChannel) override final { - if (inputChannel >= mNumDefaultInputs) return GetInputNameExtra(inputChannel - mNumDefaultInputs); - if (inputChannel >= 0) return defaultInStrings[static_cast(inputChannel)]; - return ""; - } - virtual std::string_view GetOutputName(int8_t outputChannel) override final { - if (outputChannel >= mNumDefaultOutputs) return GetOutputNameExtra(outputChannel - mNumDefaultOutputs); - return defaultOutStrings[outputChannel]; - } - virtual std::string_view GetName() override { - return mName; - } - virtual std::string_view GetInputNameExtra(int8_t) { return ""; }; - virtual std::string_view GetOutputNameExtra(int8_t) { return ""; }; virtual void ResetInput() {}; virtual void ResetSignal() {}; virtual void UpdateSignal(std::vector&, std::vector&) {}; virtual float ProcessSignal(float deltaTime, float freq) = 0; protected: - std::string mName; float mReset = 0.0f; float mFreq = 0.0f; @@ -79,15 +72,13 @@ namespace l::nodegraph { class GraphSignalSine2 : public GraphSignalBase { public: GraphSignalSine2(NodeGraphBase* node) : - GraphSignalBase(node, "Sine 2", 2) - {} - std::string extraString[2] = { "Fmod", "Phase" }; + GraphSignalBase(node, "Sine 2") + { + AddInput("Fmod", 0.0f, 1, 0.0f, 1.0f); + AddInput("Phase", 0.0f, 1, 0.0f, 1.0f); + } virtual ~GraphSignalSine2() = default; - virtual std::string_view GetInputNameExtra(int8_t extraInputChannel) override { - if(extraInputChannel < 2) return extraString[static_cast(extraInputChannel)]; - return ""; - } void ResetInput() override; void ResetSignal() override; void UpdateSignal(std::vector& inputs, std::vector& outputs) override; @@ -124,15 +115,13 @@ namespace l::nodegraph { class GraphSignalSaw2 : public GraphSignalBase { public: GraphSignalSaw2(NodeGraphBase* node) : - GraphSignalBase(node, "Saw 2", 2) - {} - std::string extraString[2] = { "Attenuation", "Cutoff" }; + GraphSignalBase(node, "Saw 2") + { + AddInput("Attenuation", 0.0f, 1, 0.0f, 1.0f); + AddInput("Cutoff", 0.0f, 1, 0.0f, 1.0f); + } virtual ~GraphSignalSaw2() = default; - virtual std::string_view GetInputNameExtra(int8_t extraInputChannel) override { - if (extraInputChannel < 2) return extraString[static_cast(extraInputChannel)]; - return ""; - } void ResetInput() override; void ResetSignal() override; void UpdateSignal(std::vector& inputs, std::vector& outputs) override; @@ -240,26 +229,24 @@ namespace l::nodegraph { class GraphSignalSine : public NodeGraphOp { public: GraphSignalSine(NodeGraphBase* node) : - NodeGraphOp(node, 6, 1) - {} - - std::string defaultInStrings[6] = { "Freq", "Volume", "Fmod", "Phase", "Smooth", "Reset"}; - std::string defaultOutStrings[1] = { "Out"}; + NodeGraphOp(node, "Sine") + { + AddInput("Freq", 0.0f, 1, 0.0f, 22050.0f); + AddInput("Volume", 0.5f, 1, 0.0f, 5.0f); + AddInput("Fmod", 1.0f, 1, 0.0f, 2.0f); + AddInput("Phase", 0.0f, 1, 0.0f, 1.0f); + AddInput("Smooth", 1.0f, 1, 0.0f, 1.0f); + AddInput("Reset", 0.0f, 1, 0.0f, 1.0f); + AddOutput("Out"); + + mPhase = 0.0f; + mPhaseFmod = 0.0f; + mWave = 0.0f; + mVol = 0.0f; + } virtual ~GraphSignalSine() = default; - virtual void Reset() override; virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; - virtual bool IsDataVisible(int8_t) override { return true; } - virtual bool IsDataEditable(int8_t) override { return true; } - 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 "Sine"; - } protected: double mFreq = 0.0; float mVolume = 0.0f; @@ -281,26 +268,24 @@ namespace l::nodegraph { class GraphSignalSineFM : public NodeGraphOp { public: GraphSignalSineFM(NodeGraphBase* node) : - NodeGraphOp(node, 9, 1) - {} - - std::string defaultInStrings[9] = { "Freq", "Volume", "Fmod", "FmodFreq", "FmodVol", "FmodOfs", "FmodGain", "Smooth", "Reset"}; - std::string defaultOutStrings[1] = { "Out" }; + NodeGraphOp(node, "Sine FM 1") + { + AddInput("Freq", 0.0f, 1, 0.0f, 22050.0f); + AddInput("Volume", 0.5f, 1, 0.0f, 5.0f); + AddInput("Fmod", 0.0f, 1, 0.0f, 100.0f); + AddInput("FmodFreq", 0.0f, 1, 0.0f, 100.0f); + AddInput("FmodVol", 0.0f, 1, 0.0f, 100.0f); + AddInput("FmodOfs", 0.0f, 1, 0.0f, 100.0f); + AddInput("FmodGain", 0.0f, 1, 0.0f, 100.0f); + AddInput("Smooth", 1.0f, 1, 0.0f, 1.0f); + AddInput("Reset", 0.0f, 1, 0.0f, 1.0f); + AddOutput("Out", 0.0f); + + mPhase = 0.0f; + } virtual ~GraphSignalSineFM() = default; - virtual void Reset() override; virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; - virtual bool IsDataVisible(int8_t) override { return true; } - virtual bool IsDataEditable(int8_t) override { return true; } - 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 "Sine FM 1"; - } protected: double mFreq = 0.0; float mVolume = 0.0f; @@ -327,26 +312,21 @@ namespace l::nodegraph { class GraphSignalSineFM2 : public NodeGraphOp { public: GraphSignalSineFM2(NodeGraphBase* node) : - NodeGraphOp(node, 6, 1) - {} - - std::string defaultInStrings[6] = { "Freq", "Volume", "FmodVol", "FmodFreq", "Smooth", "Reset" }; - std::string defaultOutStrings[1] = { "Out" }; + NodeGraphOp(node, "Sine FM 2") + { + AddInput("Freq", 0.0f, 1, 0.0f, 22050.0f); + AddInput("Volume", 0.5f, 1, 0.0f, 5.0f); + AddInput("FmodVol", 0.0f, 1, 0.0f, 1.0f); + AddInput("FmodFreq", 0.0f, 1, 0.0f, 100.0f); + AddInput("Smooth", 1.0f, 1, 0.0f, 1.0f); + AddInput("Reset", 0.0f, 1, 0.0f, 1.0f); + AddOutput("Out", 0.0f); + + mPhase = 0.0f; + } virtual ~GraphSignalSineFM2() = default; - virtual void Reset() override; virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; - virtual bool IsDataVisible(int8_t) override { return true; } - virtual bool IsDataEditable(int8_t) override { return true; } - 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 "Sine FM 2"; - } protected: double mFreq = 0.0; float mVolume = 0.0f; @@ -368,26 +348,20 @@ namespace l::nodegraph { class GraphSignalSineFM3 : public NodeGraphOp { public: GraphSignalSineFM3(NodeGraphBase* node) : - NodeGraphOp(node, 5, 1) - {} - - std::string defaultInStrings[5] = { "Freq", "Volume", "Fmod", "Smooth", "Reset" }; - std::string defaultOutStrings[1] = { "Out" }; + NodeGraphOp(node, "Sine FM 3") + { + AddInput("Freq", 0.0f, 1, 0.0f, 22050.0f); + AddInput("Volume", 0.5f, 1, 0.0f, 5.0f); + AddInput("Fmod", 0.5f, 1, 0.0f, 1.0f); + AddInput("Smooth", 1.0f, 1, 0.0f, 1.0f); + AddInput("Reset", 0.0f, 1, 0.0f, 1.0f); + AddOutput("Out", 0.0f); + + mPhase = 0.0f; + } virtual ~GraphSignalSineFM3() = default; - virtual void Reset() override; virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; - virtual bool IsDataVisible(int8_t) override { return true; } - virtual bool IsDataEditable(int8_t) override { return true; } - 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 "Sine FM 3"; - } protected: double mFreq = 0.0; float mVolume = 0.0f; @@ -406,26 +380,21 @@ namespace l::nodegraph { class GraphSignalSaw : public NodeGraphOp { public: GraphSignalSaw(NodeGraphBase* node) : - NodeGraphOp(node, 6, 1) - {} - - std::string defaultInStrings[6] = { "Freq", "Volume", "Fmod", "Phase", "Smooth", "Reset" }; - std::string defaultOutStrings[1] = { "Out" }; + NodeGraphOp(node, "Saw") + { + AddInput("Freq", 0.0f, 1, 0.0f, 22050.0f); + AddInput("Volume", 0.5f, 1, 0.0f, 5.0f); + AddInput("Fmod", 1.0f, 1, 0.0f, 2.0f); + AddInput("Phase", 0.0f, 1, 0.0f, 1.0f); + AddInput("Smooth", 1.0f, 1, 0.0f, 1.0f); + AddInput("Reset", 0.0f, 1, 0.0f, 1.0f); + AddOutput("Out", 0.0f); + + mPhase = 0.0f; + } virtual ~GraphSignalSaw() = default; - virtual void Reset() override; virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; - virtual bool IsDataVisible(int8_t) override { return true; } - virtual bool IsDataEditable(int8_t) override { return true; } - 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 "Saw"; - } protected: double mFreq = 0.0; float mVolume = 0.0f; diff --git a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpSource.h b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpSource.h index 381b8ae8..49406114 100644 --- a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpSource.h +++ b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpSource.h @@ -25,30 +25,31 @@ namespace l::nodegraph { /*********************************************************************/ class GraphSourceConstants : public NodeGraphOp { public: + + const InputBound mModeToBoundList[4] = {InputBound::INPUT_0_TO_1, InputBound::INPUT_NEG_1_POS_1, InputBound::INPUT_0_100, InputBound::INPUT_UNBOUNDED}; + const std::string mNameList[4] = {"Constant [0,1]" , "Constant [-1,1]" , "Constant [0,100]", "Constant [-inf,inf]"}; + GraphSourceConstants(NodeGraphBase* node, int32_t mode) : - NodeGraphOp(node, 0, 4, 4), + NodeGraphOp(node, ""), mMode(mode) - {} + { + mName = mNameList[mode]; + + AddOutput("Out 1"); + AddOutput("Out 2"); + AddOutput("Out 3"); + AddOutput("Out 3"); + + auto bound = GetInputBounds(mModeToBoundList[mMode]); + AddConstant("Out 1", 0.0f, bound.first, bound.second); + AddConstant("Out 2", 0.0f, bound.first, bound.second); + AddConstant("Out 3", 0.0f, bound.first, bound.second); + AddConstant("Out 4", 0.0f, bound.first, bound.second); + } virtual ~GraphSourceConstants() = default; - virtual void Reset(); virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; virtual void Tick(int32_t, float) override; - virtual std::string_view GetName() override { - switch (mMode) { - case 0: - return "Constant [0,1]"; - case 1: - return "Constant [-1,1]"; - case 2: - return "Constant [0,100]"; - case 3: - return "Constant [-inf,inf]"; - }; - return ""; - } - virtual bool IsDataVisible(int8_t) override {return true;} - virtual bool IsDataEditable(int8_t) override {return true;} protected: int32_t mMode; float mMax = 1.0f; @@ -58,25 +59,38 @@ namespace l::nodegraph { /*********************************************************************/ class GraphSourceTime : public NodeGraphOp { public: - GraphSourceTime(NodeGraphBase* node) : - NodeGraphOp(node, 0, 2, 0) - {} + GraphSourceTime(NodeGraphBase* node, int32_t audioRate, int32_t frameRate) : + NodeGraphOp(node, "Time"), + mAudioRate(audioRate), + mFrameRate(frameRate) + { + AddOutput("Audio Tick"); + AddOutput("Frame Tick"); + AddOutput("Audio Time"); + AddOutput("Frame Time"); - std::string defaultOutStrings[2] = { "Audio Time", "Frame Time"}; + AddConstant("Audio Tick", 0.0f); + AddConstant("Frame Tick", 0.0f); + AddConstant("Audio Time", 0.0f); + AddConstant("Frame Time", 0.0f); + + AddInput("Reset", 0.0f, 1, 0.0f, 1.0f); + + mAudioTime = 0.0f; + mFrameTime = 0.0f; + mFrameTick = 0; + mAudioTick = 0; + } virtual ~GraphSourceTime() = default; - virtual void Reset() override; virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; virtual void Tick(int32_t, float) override; - virtual std::string_view GetOutputName(int8_t outputChannel) override { - return defaultOutStrings[outputChannel]; - } - virtual std::string_view GetName() override { - return "Time"; - } - virtual bool IsDataVisible(int8_t) override { return true; } - virtual bool IsDataEditable(int8_t) override { return true; } protected: + int32_t mAudioRate = 44100; + int32_t mFrameRate = 60; + + int32_t mFrameTick = 0; + int32_t mAudioTick = 0; float mAudioTime = 0.0f; float mFrameTime = 0.0f; }; diff --git a/packages/nodegraph/source/common/NodeGraphSchema.cpp b/packages/nodegraph/source/common/NodeGraphSchema.cpp index 282b1245..aec36dc9 100644 --- a/packages/nodegraph/source/common/NodeGraphSchema.cpp +++ b/packages/nodegraph/source/common/NodeGraphSchema.cpp @@ -38,7 +38,7 @@ namespace l::nodegraph { node = mMainNodeGraph.NewNode(OutputType::Default, 3); break; case 4: - node = mMainNodeGraph.NewNode(OutputType::Default); + node = mMainNodeGraph.NewNode(OutputType::ExternalOutput, mAudioOutput->GetSampleRate(), 60); break; case 50: node = mMainNodeGraph.NewNode(OutputType::Default); @@ -116,28 +116,28 @@ namespace l::nodegraph { node = mMainNodeGraph.NewNode(OutputType::Default); break; case 300: - node = mMainNodeGraph.NewNode(OutputType::Default, mKeyState); + node = mMainNodeGraph.NewNode(OutputType::Default, mKeyState); break; case 301: - node = mMainNodeGraph.NewNode(OutputType::Default, mMidiManager); + node = mMainNodeGraph.NewNode(OutputType::Default, mMidiManager); break; case 302: - node = mMainNodeGraph.NewNode(OutputType::Default, mMidiManager); + node = mMainNodeGraph.NewNode(OutputType::Default, mMidiManager); break; case 303: - node = mMainNodeGraph.NewNode(OutputType::Default, mMidiManager, 0); + node = mMainNodeGraph.NewNode(OutputType::Default, mMidiManager, 0); break; case 304: - node = mMainNodeGraph.NewNode(OutputType::Default, mMidiManager, 1); + node = mMainNodeGraph.NewNode(OutputType::Default, mMidiManager, 1); break; case 305: - node = mMainNodeGraph.NewNode(OutputType::Default, mMidiManager, 2); + node = mMainNodeGraph.NewNode(OutputType::Default, mMidiManager, 2); break; case 306: - node = mMainNodeGraph.NewNode(OutputType::Default, mMidiManager, 3); + node = mMainNodeGraph.NewNode(OutputType::Default, mMidiManager, 3); break; case 307: - node = mMainNodeGraph.NewNode(OutputType::Default, mMidiManager, 4); + node = mMainNodeGraph.NewNode(OutputType::Default, mMidiManager, 4); break; case 350: node = mMainNodeGraph.NewNode(OutputType::Default); @@ -160,8 +160,6 @@ namespace l::nodegraph { case 356: 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."; @@ -169,9 +167,6 @@ namespace l::nodegraph { break; }; - if (node != nullptr) { - node->Reset(); - } return node == nullptr ? 0 : node->GetId(); } diff --git a/packages/nodegraph/source/common/core/NodeGraphBase.cpp b/packages/nodegraph/source/common/core/NodeGraphBase.cpp index ff495612..740735f9 100644 --- a/packages/nodegraph/source/common/core/NodeGraphBase.cpp +++ b/packages/nodegraph/source/common/core/NodeGraphBase.cpp @@ -17,33 +17,21 @@ namespace l::nodegraph { } void NodeGraphBase::SetNumInputs(int8_t numInputs) { - mInputCount = numInputs; - mInputs.resize(mInputCount + mConstantCount); + mInputs.resize(numInputs); } - void NodeGraphBase::SetNumOutputs(int8_t outputCount) { - mOutputs.resize(outputCount); - } - - void NodeGraphBase::SetNumConstants(int8_t numConstants) { - mConstantCount = numConstants; - mInputs.resize(mInputCount + mConstantCount); + void NodeGraphBase::SetNumOutputs(int8_t numOutputs) { + mOutputs.resize(numOutputs); } int8_t NodeGraphBase::GetNumInputs() { - ASSERT(mInputs.size() == static_cast(mInputCount + mConstantCount)); - return static_cast(mInputCount); + return static_cast(mInputs.size()); } int8_t NodeGraphBase::GetNumOutputs() { return static_cast(mOutputs.size()); } - int8_t NodeGraphBase::GetNumConstants() { - ASSERT(mInputs.size() == static_cast(mInputCount + mConstantCount)); - return static_cast(mConstantCount); - } - void NodeGraphBase::Reset() { for (auto& link : mInputs) { if (link.mInputType == InputType::INPUT_NODE && link.mInput.mInputNode != nullptr) { @@ -93,11 +81,11 @@ namespace l::nodegraph { } float& NodeGraphBase::GetOutput(int8_t outputChannel, int32_t size) { - return mOutputs.at(outputChannel).GetOutput(size); + return mOutputs.at(outputChannel).Get(size); } int32_t NodeGraphBase::GetOutputSize(int8_t outputChannel) { - return mOutputs.at(outputChannel).GetOutputSize(); + return mOutputs.at(outputChannel).GetSize(); } float NodeGraphBase::GetInput(int8_t inputChannel) { @@ -175,7 +163,7 @@ namespace l::nodegraph { } auto& input = mInputs.at(inputChannel); constant = l::math::functions::clamp(constant, input.mBoundMin, input.mBoundMax); - if (size <= 0) { + if (size <= 1) { input.mInput.mInputFloatConstant = constant; input.mInputType = InputType::INPUT_CONSTANT; input.mInputFromOutputChannel = 0; @@ -218,40 +206,21 @@ namespace l::nodegraph { return false; } auto& input = mInputs.at(inputChannel); - input.mInputBound = bound; - switch (bound) { - case InputBound::INPUT_0_TO_1: - input.mBoundMin = 0.0f; - input.mBoundMax = 1.0f; - break; - case InputBound::INPUT_0_TO_2: - input.mBoundMin = 0.0f; - input.mBoundMax = 2.0f; - break; - case InputBound::INPUT_NEG_1_POS_1: - input.mBoundMin = -1.0f; - input.mBoundMax = 1.0f; - break; - case InputBound::INPUT_0_100: - input.mBoundMin = 0.0f; - input.mBoundMax = 100.0f; - break; - case InputBound::INPUT_CUSTOM: + + if (bound == InputBound::INPUT_DONTCHANGE) { + }else if (bound == InputBound::INPUT_CUSTOM) { input.mBoundMin = boundMin; input.mBoundMax = boundMax; - break; - case InputBound::INPUT_UNBOUNDED: - input.mBoundMin = -l::math::constants::FLTMAX; - input.mBoundMax = l::math::constants::FLTMAX; - break; - case InputBound::INPUT_DONTCHANGE: - break; + } + else { + auto [min, max] = GetInputBounds(bound); + input.mBoundMin = min; + input.mBoundMax = max; } return true; } - - bool NodeGraphBase::RemoveInput(void* source) { + bool NodeGraphBase::DetachInput(void* source) { int32_t sourceRemoved = 0; for (auto& it : mInputs) { if ((it.mInputType == InputType::INPUT_NODE && it.mInput.mInputNode == source) || @@ -263,6 +232,10 @@ namespace l::nodegraph { return sourceRemoved > 0 ? true : false; } + bool NodeGraphBase::IsDataConstant(int8_t) { + return false; + } + bool NodeGraphBase::IsDataVisible(int8_t) { return false; } @@ -272,7 +245,7 @@ namespace l::nodegraph { } bool NodeGraphBase::IsOutputPolled(int8_t outputChannel) { - return mOutputs.at(outputChannel).IsOutputPolled(); + return mOutputs.at(outputChannel).IsPolled(); } OutputType NodeGraphBase::GetOutputType() { @@ -315,17 +288,19 @@ namespace l::nodegraph { } } - - void NodeGraphOp::SetNumInputs(int8_t numInputs) { - mNumInputs = numInputs; - } - - void NodeGraphOp::SetNumOutputs(int8_t numOutputs) { - mNumOutputs = numOutputs; - } - - void NodeGraphOp::SetNumConstants(int8_t numConstants) { - mNumConstants = numConstants; + void NodeGraphOp::Reset() { + for (int8_t i = 0; i < mDefaultInData.size(); i++) { + auto& e = mDefaultInData.at(i); + mNode->SetInput(i, std::get<0>(e), std::get<1>(e)); + mNode->SetInputBound(i, l::nodegraph::InputBound::INPUT_CUSTOM, std::get<2>(e), std::get<3>(e)); + } + for (int8_t i = 0; i < mDefaultOutData.size(); i++) { + auto& e = mDefaultOutData.at(i); + auto output = &mNode->GetOutput(i, std::get<1>(e)); + for (int32_t j = 0; j < std::get<1>(e);j++) { + *output++ = std::get<0>(e); + } + } } int8_t NodeGraphOp::GetNumInputs() { @@ -336,28 +311,47 @@ namespace l::nodegraph { return mNumOutputs; } - int8_t NodeGraphOp::GetNumConstants() { - return mNumConstants; + bool NodeGraphOp::IsDataConstant(int8_t channel) { + return std::get<4>(mDefaultInData.at(channel)); } - bool NodeGraphOp::IsDataVisible(int8_t) { - return false; + bool NodeGraphOp::IsDataVisible(int8_t channel) { + return std::get<5>(mDefaultInData.at(channel)); } - bool NodeGraphOp::IsDataEditable(int8_t) { - return false; + bool NodeGraphOp::IsDataEditable(int8_t channel) { + return std::get<6>(mDefaultInData.at(channel)); } std::string_view NodeGraphOp::GetInputName(int8_t inputChannel) { - return defaultInStrings[inputChannel]; + return mDefaultInStrings[inputChannel]; } std::string_view NodeGraphOp::GetOutputName(int8_t outputChannel) { - return defaultOutStrings[outputChannel]; + return mDefaultOutStrings[outputChannel]; } std::string_view NodeGraphOp::GetName() { - return ""; + return mName; } + void NodeGraphOp::AddInput(std::string_view name, float defaultValue, int32_t size, float boundMin, float boundMax, bool visible, bool editable) { + mNumInputs++; + mDefaultInStrings.push_back(std::string(name)); + mDefaultInData.push_back({ defaultValue, size, boundMin, boundMax, false, visible, editable}); + } + + void NodeGraphOp::AddOutput(std::string_view name, float defaultValue, int32_t size) { + mNumOutputs++; + mDefaultOutStrings.push_back(std::string(name)); + mDefaultOutData.push_back({ defaultValue, size }); + } + + void NodeGraphOp::AddConstant(std::string_view name, float defaultValue, float boundMin, float boundMax, bool visible, bool editable) { + mNumInputs++; + mDefaultInStrings.push_back(std::string(name)); + mDefaultInData.push_back({ defaultValue, 1, boundMin, boundMax, true, visible, editable}); + } + + } \ No newline at end of file diff --git a/packages/nodegraph/source/common/core/NodeGraphGroup.cpp b/packages/nodegraph/source/common/core/NodeGraphGroup.cpp index 7c6a3aab..e11e401d 100644 --- a/packages/nodegraph/source/common/core/NodeGraphGroup.cpp +++ b/packages/nodegraph/source/common/core/NodeGraphGroup.cpp @@ -14,51 +14,54 @@ namespace l::nodegraph { } void NodeGraphGroup::SetNumInputs(int8_t numInputs) { - mInputNode.SetNumInputs(numInputs); - mInputNode.SetNumOutputs(numInputs); + if (!mInputNode) { + mInputNode = NewNode(OutputType::Default, numInputs); + } } - void NodeGraphGroup::SetNumOutputs(int8_t outputCount) { - mOutputNode.SetNumInputs(outputCount); - mOutputNode.SetNumOutputs(outputCount); + void NodeGraphGroup::SetNumOutputs(int8_t numOutput) { + if (!mOutputNode) { + mOutputNode = NewNode(OutputType::ExternalOutput, numOutput); + mOutputNodes.push_back(mOutputNode); + } } void NodeGraphGroup::SetInput(int8_t inputChannel, NodeGraphBase& source, int8_t sourceOutputChannel) { - mInputNode.SetInput(inputChannel, source, sourceOutputChannel); + mInputNode->SetInput(inputChannel, source, sourceOutputChannel); } void NodeGraphGroup::SetInput(int8_t inputChannel, NodeGraphGroup& source, int8_t sourceOutputChannel) { - mInputNode.SetInput(inputChannel, source, sourceOutputChannel); + mInputNode->SetInput(inputChannel, source, sourceOutputChannel); } void NodeGraphGroup::SetInput(int8_t inputChannel, float constant) { - mInputNode.SetInput(inputChannel, constant); + mInputNode->SetInput(inputChannel, constant); } void NodeGraphGroup::SetInput(int8_t inputChannel, float* floatPtr) { - mInputNode.SetInput(inputChannel, floatPtr); + mInputNode->SetInput(inputChannel, floatPtr); } void NodeGraphGroup::SetOutput(int8_t outputChannel, NodeGraphBase& source, int8_t sourceOutputChannel) { - mOutputNode.SetInput(outputChannel, source, sourceOutputChannel); - mOutputNode.SetOutputName(outputChannel, source.GetOutputName(sourceOutputChannel)); + mOutputNode->SetInput(outputChannel, source, sourceOutputChannel); + mOutputNode->SetOutputName(outputChannel, source.GetOutputName(sourceOutputChannel)); } void NodeGraphGroup::SetOutput(int8_t outputChannel, NodeGraphGroup& source, int8_t sourceOutputChannel) { - mOutputNode.SetInput(outputChannel, source, sourceOutputChannel); - mOutputNode.SetOutputName(outputChannel, source.GetOutputNode().GetOutputName(sourceOutputChannel)); + mOutputNode->SetInput(outputChannel, source, sourceOutputChannel); + mOutputNode->SetOutputName(outputChannel, source.GetOutputNode().GetOutputName(sourceOutputChannel)); } float NodeGraphGroup::GetOutput(int8_t outputChannel) { - return mOutputNode.GetOutput(outputChannel); + return mOutputNode->GetOutput(outputChannel); } NodeGraphBase& NodeGraphGroup::GetInputNode() { - return mInputNode; + return *mInputNode; } NodeGraphBase& NodeGraphGroup::GetOutputNode() { - return mOutputNode; + return *mOutputNode; } bool NodeGraphGroup::ContainsNode(int32_t id) { @@ -91,7 +94,7 @@ namespace l::nodegraph { auto node = GetNode(id); int32_t sourceCount = 0; for (auto& it : mNodes) { - if (it->RemoveInput(node)) { + if (it->DetachInput(node)) { sourceCount++; } } @@ -111,7 +114,7 @@ namespace l::nodegraph { } void NodeGraphGroup::ClearProcessFlags() { - mOutputNode.ClearProcessFlags(); + mOutputNode->ClearProcessFlags(); } void NodeGraphGroup::ProcessSubGraph(int32_t numSamples, bool) { diff --git a/packages/nodegraph/source/common/core/NodeGraphInput.cpp b/packages/nodegraph/source/common/core/NodeGraphInput.cpp index 51456423..80fa6eef 100644 --- a/packages/nodegraph/source/common/core/NodeGraphInput.cpp +++ b/packages/nodegraph/source/common/core/NodeGraphInput.cpp @@ -6,6 +6,23 @@ #include "math/MathFunc.h" namespace l::nodegraph { + + std::pair GetInputBounds(InputBound bound) { + switch (bound) { + case InputBound::INPUT_0_TO_1: + return { 0.0f, 1.0f }; + case InputBound::INPUT_0_TO_2: + return { 0.0f, 2.0f }; + case InputBound::INPUT_NEG_1_POS_1: + return { -1.0f, 1.0f }; + case InputBound::INPUT_0_100: + return { 0.0f, 100.0f }; + case InputBound::INPUT_UNBOUNDED: + return { -l::math::constants::FLTMAX, l::math::constants::FLTMAX }; + } + return { -l::math::constants::FLTMAX, l::math::constants::FLTMAX }; + } + void NodeGraphInput::Reset() { if (mInputType == InputType::INPUT_NODE || mInputType == InputType::INPUT_VALUE) { mInput.mInputNode = nullptr; @@ -68,4 +85,15 @@ namespace l::nodegraph { return mInput.mInputFloatConstant; } + int32_t NodeGraphInput::GetSize() { + if (!mInputBuf) { + return 1; + } + else { + return static_cast(mInputBuf->size()); + } + } + + + } \ No newline at end of file diff --git a/packages/nodegraph/source/common/core/NodeGraphOutput.cpp b/packages/nodegraph/source/common/core/NodeGraphOutput.cpp index 1d1eff1c..c7e56a23 100644 --- a/packages/nodegraph/source/common/core/NodeGraphOutput.cpp +++ b/packages/nodegraph/source/common/core/NodeGraphOutput.cpp @@ -5,7 +5,7 @@ #include "math/MathFunc.h" namespace l::nodegraph { - float& NodeGraphOutput::GetOutput(int32_t size) { + float& NodeGraphOutput::Get(int32_t size) { if (!mOutputBuf) { if (size <= 1) { mOutputPolled = true; @@ -22,7 +22,7 @@ namespace l::nodegraph { return *mOutputBuf->data(); } - int32_t NodeGraphOutput::GetOutputSize() { + int32_t NodeGraphOutput::GetSize() { if (!mOutputBuf) { return 1; } @@ -31,11 +31,11 @@ namespace l::nodegraph { } } - bool NodeGraphOutput::IsOutputPolled() { + bool NodeGraphOutput::IsPolled() { return mOutputPolled; } - void NodeGraphOutput::ResetOutputPollState() { + void NodeGraphOutput::ResetPollState() { mOutputPolled = false; } diff --git a/packages/nodegraph/source/common/operations/NodeGraphOpEffect.cpp b/packages/nodegraph/source/common/operations/NodeGraphOpEffect.cpp index a6c92ccb..d45ee7a3 100644 --- a/packages/nodegraph/source/common/operations/NodeGraphOpEffect.cpp +++ b/packages/nodegraph/source/common/operations/NodeGraphOpEffect.cpp @@ -10,33 +10,9 @@ namespace l::nodegraph { - /*********************************************************************/ - void GraphEffectReverb1::Reset() { - // { "In 1", "In 2", "Mix", "Attenuation", "Room Size", "Delay 1", "Feedback 1", "Delay 2", "Feedback 2", "Delay 3", "Feedback 3" }; - - mNode->SetInput(2, 0.75f); - mNode->SetInput(3, 0.5f); - mNode->SetInput(4, 30.0f); - mNode->SetInput(5, 0.5f); - mNode->SetInput(6, 0.9f); - mNode->SetInput(7, 0.8f); - mNode->SetInput(8, 0.9f); - mNode->SetInput(9, 0.7f); - mNode->SetInput(10, 0.9f); - mNode->SetInputBound(2, InputBound::INPUT_0_TO_1); - mNode->SetInputBound(3, InputBound::INPUT_0_TO_1); - mNode->SetInputBound(4, InputBound::INPUT_CUSTOM, 0.2f, maxRoomSizeInMeters); - mNode->SetInputBound(5, InputBound::INPUT_0_TO_1); - mNode->SetInputBound(6, InputBound::INPUT_0_TO_1); - mNode->SetInputBound(7, InputBound::INPUT_0_TO_1); - mNode->SetInputBound(8, InputBound::INPUT_0_TO_1); - mNode->SetInputBound(9, InputBound::INPUT_0_TO_1); - mNode->SetInputBound(10, InputBound::INPUT_0_TO_1); - } + void GraphEffectReverb1::Process(int32_t numSamples, std::vector&inputs, std::vector&outputs) { + auto wet = &inputs.at(2).Get(numSamples); - void GraphEffectReverb1::Process(int32_t, std::vector&inputs, std::vector&outputs) { - float wet = inputs.at(2).Get(); - fb = 0.2f * (1.0f - inputs.at(3).Get()); float roomSize = inputs.at(4).Get(); @@ -49,7 +25,7 @@ namespace l::nodegraph { d2 = inputs.at(9).Get(); fb2 = 0.5f * 0.5f * math::functions::max(inputs.at(10).Get(), 1.0f); - float dry = 1.0f - wet; + float dry = 1.0f - *wet; uint32_t delay0 = (int(bufIndex + d0 * bufSizeLimit)) % bufSizeLimit; uint32_t delay1 = (int(bufIndex + d1 * bufSizeLimit)) % bufSizeLimit; @@ -57,8 +33,8 @@ namespace l::nodegraph { float in0 = inputs.at(0).Get(); float in1 = inputs.at(1).Get(); - outputs[0].mOutput = in0 * dry + (fb1 * buf1[delay1] + fb0 * buf0[delay0] + fb2 * buf0[delay2]) * wet; - outputs[1].mOutput = in1 * dry + (fb1 * buf0[delay1] + fb0 * buf1[delay0] + fb2 * buf1[delay2]) * wet; + outputs[0].mOutput = in0 * dry + (fb1 * buf1[delay1] + fb0 * buf0[delay0] + fb2 * buf0[delay2]) * *wet; + outputs[1].mOutput = in1 * dry + (fb1 * buf0[delay1] + fb0 * buf1[delay0] + fb2 * buf1[delay2]) * *wet; buf0[bufIndex] = fb * buf1[bufIndex] - fb1 * buf1[delay1] - fb0 * buf0[delay0] - fb2 * buf0[delay2] + in0; buf1[bufIndex] = fb * buf0[bufIndex] - fb1 * buf0[delay1] - fb0 * buf1[delay0] - fb2 * buf1[delay2] + in1; @@ -72,30 +48,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); - mNode->SetInput(5, 0.5f); - mNode->SetInput(6, 0.1f); - mNode->SetInput(7, 0.8f); - mNode->SetInput(8, 5.0f); - mNode->SetInput(9, 0.7f); - mNode->SetInput(10, 0.95f); - mNode->SetInput(11, 0.01f); - mNode->SetInputBound(2, InputBound::INPUT_0_TO_1); - mNode->SetInputBound(3, InputBound::INPUT_0_TO_1); - mNode->SetInputBound(4, InputBound::INPUT_CUSTOM, 1.0f, 334.0f); - mNode->SetInputBound(5, InputBound::INPUT_0_TO_1); - mNode->SetInputBound(6, InputBound::INPUT_CUSTOM, 0.0f, 10.0f); - mNode->SetInputBound(7, InputBound::INPUT_CUSTOM, 0.0f, 10.0f); - mNode->SetInputBound(8, InputBound::INPUT_CUSTOM, 1.0f, 30.0f); - mNode->SetInputBound(9, InputBound::INPUT_CUSTOM, 1.0f, 10.0f); - mNode->SetInputBound(10, InputBound::INPUT_CUSTOM, 0.001f, 0.999f); - mNode->SetInputBound(11, InputBound::INPUT_CUSTOM, 0.001f, 0.999f); - } - void GraphEffectReverb2::Process(int32_t, std::vector& inputs, std::vector& outputs) { float wet = inputs.at(2).Get(); float reverbFeedback = inputs.at(3).Get(); @@ -211,18 +163,6 @@ namespace l::nodegraph { } /*********************************************************************/ - 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(int32_t, std::vector& inputs, std::vector& outputs) { float attackMs = inputs .at(2).Get(); @@ -264,17 +204,6 @@ namespace l::nodegraph { } /*********************************************************************/ - void GraphEffectEnvelope::Reset() { - mEnvelope = 0.0f; - mNode->SetInput(1, 0.5f); - mNode->SetInput(2, 50.0f); - mNode->SetInput(3, 50.0f); - mNode->SetInput(4, 0.1f); - mNode->SetInputBound(1, InputBound::INPUT_0_TO_1); - mNode->SetInputBound(2, InputBound::INPUT_CUSTOM, 1.0f, 100000.0f); - mNode->SetInputBound(3, InputBound::INPUT_CUSTOM, 1.0f, 100000.0f); - mNode->SetInputBound(4, InputBound::INPUT_CUSTOM, 0.0001f, 1.0f); - } void GraphEffectEnvelope::Process(int32_t, std::vector& inputs, std::vector& outputs) { float freqTarget = inputs.at(0).Get(); @@ -343,14 +272,6 @@ namespace l::nodegraph { } /*********************************************************************/ - 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(int32_t, std::vector& inputs, std::vector& outputs) { float attackMs = inputs.at(2).Get(); float releaseMs = inputs.at(3).Get(); @@ -373,19 +294,6 @@ namespace l::nodegraph { } /*********************************************************************/ - 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(int32_t, std::vector& inputs, std::vector& outputs) { float wet = inputs.at(2).Get(); float preamp = inputs.at(3).Get(); @@ -422,23 +330,6 @@ namespace l::nodegraph { } /*********************************************************************/ - 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->SetInput(6, 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); - mNode->SetInputBound(6, InputBound::INPUT_0_TO_1); - } - void GraphEffectTranceGate::Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) { // "Bpm", "Fmod", "Attack", "Pattern" @@ -488,32 +379,9 @@ namespace l::nodegraph { } /*********************************************************************/ - void GraphEffectArpeggio::Reset() { - // { "Note On Id", "Note Off Id", "Velocity", "Bpm", "Fmod", "Attack"}; - - mGainTarget = 0.0f; - - mNode->SetInput(0, l::audio::gNoNote_f, 8); - mNode->SetInput(1, l::audio::gNoNote_f, 8); - - mNode->SetInput(2, 1.0f); - mNode->SetInput(3, 60.0f); - mNode->SetInput(4, 1.0f); - mNode->SetInput(5, 0.01f); - mNode->SetInput(6, 0.0f); - - mNode->SetInputBound(2, InputBound::INPUT_0_TO_1); - mNode->SetInputBound(3, InputBound::INPUT_CUSTOM, 1.0f, 1000.0f); - mNode->SetInputBound(4, InputBound::INPUT_CUSTOM, 0.01f, 1.0f); - mNode->SetInputBound(5, InputBound::INPUT_0_TO_1); - mNode->SetInputBound(6, InputBound::INPUT_0_TO_1); - } - void GraphEffectArpeggio::Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) { - // "Note On Id", "Note Off Id", "Velocity", "Bpm", "Fmod", "Attack" - - auto noteIdsOn = &inputs.at(0).Get(8); - auto noteIdsOff = &inputs.at(1).Get(8); + auto noteIdsOn = &inputs.at(0).Get(gPolyphony); + auto noteIdsOff = &inputs.at(1).Get(gPolyphony); float velocity = inputs.at(2).Get(); float bpm = inputs.at(3).Get(); float fmod = inputs.at(4).Get(); @@ -524,7 +392,7 @@ namespace l::nodegraph { } if (!mNotes.empty()) { - for (int32_t i = 0; i < 8; i++) { + for (int32_t i = 0; i < gPolyphony; i++) { if (l::math::functions::equal(*noteIdsOff, l::audio::gNoNote_f)) { break; } @@ -541,7 +409,7 @@ namespace l::nodegraph { mSamplesUntilUpdate = 0.0f; mGainTarget = 0.0f; } - for (int32_t i = 0; i < 8; i++) { + for (int32_t i = 0; i < gPolyphony; i++) { if (l::math::functions::equal(*noteIdsOn, l::audio::gNoNote_f)) { break; } diff --git a/packages/nodegraph/source/common/operations/NodeGraphOpFilter.cpp b/packages/nodegraph/source/common/operations/NodeGraphOpFilter.cpp index aa075a02..e5451e90 100644 --- a/packages/nodegraph/source/common/operations/NodeGraphOpFilter.cpp +++ b/packages/nodegraph/source/common/operations/NodeGraphOpFilter.cpp @@ -32,7 +32,7 @@ namespace l::nodegraph { void GraphFilterBase::Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) { auto input = &inputs.at(1).Get(numSamples); - auto output = &outputs.at(0).GetOutput(numSamples); + auto output = &outputs.at(0).Get(numSamples); mSamplesUntilUpdate = l::audio::BatchUpdate(mUpdateSamples, mSamplesUntilUpdate, 0, numSamples, [&]() { @@ -42,7 +42,7 @@ namespace l::nodegraph { } mCutoff = inputs.at(2).Get(); - mCutoffCPS = l::math::functions::sin(l::math::constants::PI_f * mCutoff * mCutoff / 2.0f); + mCutoffCPS = l::math::functions::sin(l::math::constants::PI_f * mCutoff * mCutoff * 0.5f); mResonance = 1.0f - inputs.at(3).Get(); mCutoffFilter.SetConvergence().SetTarget(mCutoffCPS).SnapAt(); diff --git a/packages/nodegraph/source/common/operations/NodeGraphOpInput.cpp b/packages/nodegraph/source/common/operations/NodeGraphOpInput.cpp index f8f2550b..2c79766a 100644 --- a/packages/nodegraph/source/common/operations/NodeGraphOpInput.cpp +++ b/packages/nodegraph/source/common/operations/NodeGraphOpInput.cpp @@ -2,6 +2,7 @@ #include "logging/Log.h" #include "audio/AudioUtils.h" +#include "hid/Midi.h" #include "math/MathFunc.h" @@ -10,20 +11,11 @@ namespace l::nodegraph { /*********************************************************************/ - void GraphInputKeyboardPiano::Reset() { - mNode->SetInput(0, 0.0f); - mNode->SetInput(1, l::audio::gNoNote_f, 8); - mNode->SetInput(2, l::audio::gNoNote_f, 8); - - mNode->GetOutput(1, 8); - mNode->GetOutput(2, 8); - } - void GraphInputKeyboardPiano::Process(int32_t, std::vector& inputs, std::vector& outputs) { outputs.at(0).mOutput = inputs.at(0).Get(); - auto output1 = &outputs.at(1).GetOutput(8); - auto output2 = &outputs.at(2).GetOutput(8); + auto output1 = &outputs.at(1).Get(8); + auto output2 = &outputs.at(2).Get(8); auto input1 = &inputs.at(1).Get(8); auto input2 = &inputs.at(2).Get(8); for (int32_t i = 0; i < 8; i++) { @@ -114,26 +106,15 @@ namespace l::nodegraph { } /*********************************************************************/ - void GraphInputMidiKeyboard::Reset() { - mNode->SetInput(0, 0.0f); - mNode->SetInput(1, 0.0f); - mNode->SetInput(2, l::audio::gNoNote_f, 8); - mNode->SetInput(3, l::audio::gNoNote_f, 8); - mNode->SetInput(4, 0.0f); - - mNode->GetOutput(2, 8); - mNode->GetOutput(3, 8); - } - void GraphInputMidiKeyboard::Process(int32_t, std::vector& inputs, std::vector& outputs) { outputs.at(0).mOutput = inputs.at(0).Get(); outputs.at(1).mOutput = inputs.at(1).Get(); - auto output2 = &outputs.at(2).GetOutput(8); - auto output3 = &outputs.at(3).GetOutput(8); - auto input2 = &inputs.at(2).Get(8); - auto input3 = &inputs.at(3).Get(8); - for (int32_t i = 0; i < 8; i++) { + auto output2 = &outputs.at(2).Get(gPolyphony); + auto output3 = &outputs.at(3).Get(gPolyphony); + auto input2 = &inputs.at(2).Get(gPolyphony); + auto input3 = &inputs.at(3).Get(gPolyphony); + for (int32_t i = 0; i < gPolyphony; i++) { *output2++ = *input2; *output3++ = *input3; *input2++ = l::audio::gNoNote_f; @@ -178,15 +159,15 @@ namespace l::nodegraph { mLastNote = note; float frequency = l::audio::GetFrequencyFromNote(static_cast(note)); mNode->SetInput(0, frequency); - mNode->SetInput(1, velocity / 128.0f); + mNode->SetInput(1, velocity / 127.0f); if (mSustain && !mSustainedNotes.empty()) { std::erase_if(mSustainedNotes, [&](const int32_t& sustainedNote) { return sustainedNote == note; }); } - auto input2 = &mNode->GetInput(2, 8); - for (int32_t i = 0; i < 8; i++) { + auto input2 = &mNode->GetInput(2, gPolyphony); + for (int32_t i = 0; i < gPolyphony; i++) { if (l::math::functions::equal(*input2, l::audio::gNoNote_f)) { *input2 = static_cast(note); break; @@ -203,8 +184,8 @@ namespace l::nodegraph { } if (!mSustain) { - auto input3 = &mNode->GetInput(3, 8); - for (int32_t i = 0; i < 8; i++) { + auto input3 = &mNode->GetInput(3, gPolyphony); + for (int32_t i = 0; i < gPolyphony; i++) { if (l::math::functions::equal(*input3, l::audio::gNoNote_f)) { *input3 = static_cast(note); break; @@ -221,8 +202,8 @@ namespace l::nodegraph { mSustain = on; if (!mSustain && !mSustainedNotes.empty()) { for (auto note : mSustainedNotes) { - auto input3 = &mNode->GetInput(3, 8); - for (int32_t i = 0; i < 8; i++) { + auto input3 = &mNode->GetInput(3, gPolyphony); + for (int32_t i = 0; i < gPolyphony; i++) { if (l::math::functions::equal(*input3, l::audio::gNoNote_f)) { *input3 = static_cast(note); break; @@ -275,13 +256,6 @@ namespace l::nodegraph { } /*********************************************************************/ - void GraphInputMidiKnobs::Reset() { - for (int8_t i = 0; i < GetNumInputs(); i++) { - mNode->SetInput(i, 0.0f); - } - mNode->SetInput(8, 0.0f); - } - void GraphInputMidiKnobs::Process(int32_t, std::vector& inputs, std::vector& outputs) { for (size_t i = 0; i < inputs.size(); i++) { outputs.at(i).mOutput = inputs.at(i).Get(); @@ -299,23 +273,16 @@ namespace l::nodegraph { } if (data.status == 11) { if (data.data1 >= 48 && data.data1 < 56) { - mNode->SetInput(static_cast(data.data1 - 48), data.data2 / 128.0f); + mNode->SetInput(static_cast(data.data1 - 48), data.data2 / 127.0f); } else if (data.data1 >= 7 && data.data1 < 15) { - mNode->SetInput(static_cast(data.data1 - 7), data.data2 / 128.0f); + mNode->SetInput(static_cast(data.data1 - 7), data.data2 / 127.0f); } } } /*********************************************************************/ - void GraphInputMidiButtons::Reset() { - for (int8_t i = 0; i < GetNumInputs(); i++) { - mNode->SetInput(i, 0.0f); - } - mNode->SetInput(8, 0.0f); - } - void GraphInputMidiButtons::Process(int32_t, std::vector& inputs, std::vector& outputs) { for (size_t i = 0; i < inputs.size(); i++) { outputs.at(i).mOutput = inputs.at(i).Get(); diff --git a/packages/nodegraph/source/common/operations/NodeGraphOpOutput.cpp b/packages/nodegraph/source/common/operations/NodeGraphOpOutput.cpp index f15c0228..956982e1 100644 --- a/packages/nodegraph/source/common/operations/NodeGraphOpOutput.cpp +++ b/packages/nodegraph/source/common/operations/NodeGraphOpOutput.cpp @@ -10,12 +10,6 @@ namespace l::nodegraph { /*********************************************************************/ - void GraphOutputDebug::Reset() { - mValue = 0.0; - mNode->SetInput(1, 0.5f); - mNode->SetInputBound(1, InputBound::INPUT_0_TO_1); - } - void GraphOutputDebug::Process(int32_t, std::vector& inputs, std::vector&) { float value = inputs.at(0).Get(); float friction = inputs.at(1).Get(); @@ -24,12 +18,6 @@ namespace l::nodegraph { } /*********************************************************************/ - void GraphOutputSpeaker::Reset() { - mEnvelope = 0.0f; - mNode->SetInput(2, 0.5f); - mNode->SetInputBound(2, InputBound::INPUT_0_TO_1); - } - void GraphOutputSpeaker::Process(int32_t, std::vector& inputs, std::vector&) { auto& buffer = mAudioStream->GetWriteBuffer(); @@ -37,88 +25,55 @@ namespace l::nodegraph { float in1 = inputs.at(1).Get(); float volume = inputs.at(2).Get(); - volume *= volume; - - if (false) { // limiter, causes distorsion - float limit = volume; - volume *= 0.95f; - - float inPreamp0 = in0 * volume; - float inPreamp1 = in1 * volume; - - if (inPreamp0 >= limit || inPreamp0 <= -limit) { - if (inPreamp0 > 0.0f) { - inPreamp0 = limit + (1.0f - limit) * l::math::functions::sigmoidFast((inPreamp0 - limit) / ((1.0f - limit) * 1.5f)); - } - else { - inPreamp0 = -(limit + (1.0f - limit) * l::math::functions::sigmoidFast((-inPreamp0 - limit) / ((1.0f - limit) * 1.5f))); - } - } - if (inPreamp1 >= limit || inPreamp1 <= -limit) { - if (inPreamp1 > 0.0f) { - inPreamp1 = limit + (1.0f - limit) * l::math::functions::sigmoidFast((inPreamp1 - limit) / ((1.0f - limit) * 1.5f)); - } - else { - inPreamp1 = -(limit + (1.0f - limit) * l::math::functions::sigmoidFast((-inPreamp1 - limit) / ((1.0f - limit) * 1.5f))); - } - } - buffer[mCurrentStereoPosition++] = inPreamp0; - buffer[mCurrentStereoPosition++] = inPreamp1; + mFilterEnvelope.SetTarget(volume * volume).SnapAt(); + + float attackMs = 40.0f; + float releaseMs = 40.0f; + 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 limit = mFilterEnvelope.Next(); + float limitedVolume = 0.95f * limit; + + float inVal0 = in0 * limitedVolume; + float inVal1 = in1 * limitedVolume; + float inVal = inVal0 > inVal1 ? inVal0 : inVal1; + if (inVal > mEnvelope) { + mEnvelope = attack * (mEnvelope - inVal) + inVal; } else { - float attackMs = 40.0f; - float releaseMs = 40.0f; - 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 limit = volume; - volume *= 0.95f; - - float inVal0 = in0 * volume; - float inVal1 = in1 * volume; - float inVal = inVal0 > inVal1 ? inVal0 : inVal1; - if (inVal > mEnvelope) { - mEnvelope = attack * (mEnvelope - inVal) + inVal; - } - else { - mEnvelope = release * (mEnvelope - inVal) + inVal; - } + mEnvelope = release * (mEnvelope - inVal) + inVal; + } - float out0 = 0.0f; - float out1 = 0.0f; - float envelopeAbs = l::math::functions::abs(mEnvelope); - if (envelopeAbs > limit) { - if (envelopeAbs > 1.0f) { - out0 = inVal0 / mEnvelope; - out1 = inVal1 / mEnvelope; - } - else { - out0 = inVal0 / (1.0f + mEnvelope - limit); - out1 = inVal1 / (1.0f + mEnvelope - limit); - } + float out0 = 0.0f; + float out1 = 0.0f; + float envelopeAbs = l::math::functions::abs(mEnvelope); + if (envelopeAbs > limit) { + if (envelopeAbs > 1.0f) { + out0 = inVal0 / mEnvelope; + out1 = inVal1 / mEnvelope; } else { - out0 = inVal0; - out1 = inVal1; + out0 = inVal0 / (1.0f + mEnvelope - limit); + out1 = inVal1 / (1.0f + mEnvelope - limit); } - - buffer[mCurrentStereoPosition++] = out0; - buffer[mCurrentStereoPosition++] = out1; } + else { + out0 = inVal0; + out1 = inVal1; + } + + buffer[mCurrentStereoPosition++] = out0; + buffer[mCurrentStereoPosition++] = out1; mCurrentStereoPosition %= mAudioStream->GetPartTotalSize(); } /*********************************************************************/ - void GraphOutputPlot::Reset() { - mNode->SetInput(0, 0.0f); - mNode->SetInputBound(0, InputBound::INPUT_NEG_1_POS_1); - } - void GraphOutputPlot::Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) { float* value = &inputs.at(0).Get(numSamples); - int32_t outputSize = outputs.at(0).GetOutputSize(); - float* output = &outputs.at(0).GetOutput(outputSize); + int32_t outputSize = outputs.at(0).GetSize(); + float* output = &outputs.at(0).Get(outputSize); for (int32_t i = 0; i < numSamples; i++) { output[mCurIndex] = *value++; diff --git a/packages/nodegraph/source/common/operations/NodeGraphOpSignal.cpp b/packages/nodegraph/source/common/operations/NodeGraphOpSignal.cpp index afc1ec30..bff8a405 100644 --- a/packages/nodegraph/source/common/operations/NodeGraphOpSignal.cpp +++ b/packages/nodegraph/source/common/operations/NodeGraphOpSignal.cpp @@ -10,34 +10,13 @@ namespace l::nodegraph { /*********************************************************************/ - void GraphSignalBase::Reset() { - mFreq = 0.0f; - mVolumeTarget = 0.0f; - mSmooth = 0.5f; - mDeltaTime = 0.0f; - mSamplesUntilUpdate = 0.0f; - - mNode->SetInput(0, 0.0f); - mNode->SetInput(1, 256.0f); - mNode->SetInput(2, 0.0f); // 441.0047f is stable in plot when debugging - mNode->SetInput(3, 0.5f); - mNode->SetInput(4, 0.5f); - mNode->SetInputBound(0, InputBound::INPUT_0_TO_1); - mNode->SetInputBound(1, InputBound::INPUT_CUSTOM, 1.0f, 44100.0f); - mNode->SetInputBound(2, InputBound::INPUT_CUSTOM, 0.0f, l::math::constants::FLTMAX); - mNode->SetInputBound(3, InputBound::INPUT_0_100); - mNode->SetInputBound(4, InputBound::INPUT_0_TO_1); - - ResetInput(); - } - void GraphSignalBase::Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) { float sync = inputs.at(0).Get(); if (sync > 0.5f) { mSamplesUntilUpdate = 0; } - float* output0 = &outputs.at(0).GetOutput(numSamples); + float* output0 = &outputs.at(0).Get(numSamples); mSamplesUntilUpdate = l::audio::BatchUpdate(mUpdateSamples, mSamplesUntilUpdate, 0, numSamples, [&]() { @@ -140,29 +119,8 @@ namespace l::nodegraph { } /*********************************************************************/ - void GraphSignalSine::Reset() { - // { "Freq Hz", "Freq Mod", "Phase Mod", "Reset"}; - mPhase = 0.0f; - mPhaseFmod = 0.0f; - mWave = 0.0f; - mVol = 0.0f; - - mNode->SetInput(0, 0.0f); - mNode->SetInput(1, 0.0f); - mNode->SetInput(2, 1.0f); - mNode->SetInput(3, 0.0f); - mNode->SetInput(4, 0.5f); - mNode->SetInput(5, 0.0f); - mNode->SetInputBound(0, InputBound::INPUT_CUSTOM, 0.0f, l::math::constants::FLTMAX); - mNode->SetInputBound(1, InputBound::INPUT_0_100); - mNode->SetInputBound(2, InputBound::INPUT_0_TO_2); - mNode->SetInputBound(3, InputBound::INPUT_0_TO_1); - mNode->SetInputBound(4, InputBound::INPUT_0_TO_1); - mNode->SetInputBound(5, InputBound::INPUT_0_TO_1); - } - void GraphSignalSine::Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) { - float* output0 = &outputs.at(0).GetOutput(numSamples); + float* output0 = &outputs.at(0).Get(numSamples); mSamplesUntilUpdate = l::audio::BatchUpdate(256.0f, mSamplesUntilUpdate, 0, numSamples, [&]() { @@ -232,31 +190,8 @@ namespace l::nodegraph { } /*********************************************************************/ - void GraphSignalSineFM::Reset() { - // { "Note", "Volume", "Fmod", "FmodFreq", "FmodVol", "FmodOfs", "Reset"} - mPhase = 0.0f; - mNode->SetInput(0, 0.0f); - mNode->SetInput(1, 0.5f); - mNode->SetInput(2, 0.0f); - mNode->SetInput(3, 0.0f); - mNode->SetInput(4, 0.0f); - mNode->SetInput(5, 0.0f); - mNode->SetInput(6, 0.0f); - mNode->SetInput(7, 0.5f); - mNode->SetInput(8, 0.0f); - mNode->SetInputBound(0, InputBound::INPUT_CUSTOM, 0.0f, l::math::constants::FLTMAX); - mNode->SetInputBound(1, InputBound::INPUT_0_100); - mNode->SetInputBound(2, InputBound::INPUT_UNBOUNDED); - mNode->SetInputBound(3, InputBound::INPUT_UNBOUNDED); - mNode->SetInputBound(4, InputBound::INPUT_UNBOUNDED); - mNode->SetInputBound(5, InputBound::INPUT_UNBOUNDED); - mNode->SetInputBound(6, InputBound::INPUT_0_TO_1); - mNode->SetInputBound(7, InputBound::INPUT_0_TO_1); - mNode->SetInputBound(8, InputBound::INPUT_0_TO_1); - } - void GraphSignalSineFM::Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) { - float* output0 = &outputs.at(0).GetOutput(numSamples); + float* output0 = &outputs.at(0).Get(numSamples); mSamplesUntilUpdate = l::audio::BatchUpdate(256.0f, mSamplesUntilUpdate, 0, numSamples, [&]() { @@ -308,25 +243,8 @@ namespace l::nodegraph { } /*********************************************************************/ - void GraphSignalSineFM2::Reset() { - // { "Note", "Volume", "FmodVol", "FmodOfs", "Reset"} - mPhase = 0.0f; - mNode->SetInput(0, 0.0f); - mNode->SetInput(1, 0.5f); - mNode->SetInput(2, 0.0f); - mNode->SetInput(3, 0.0f); - mNode->SetInput(4, 0.5f); - mNode->SetInput(5, 0.0f); - mNode->SetInputBound(0, InputBound::INPUT_CUSTOM, 0.0f, l::math::constants::FLTMAX); - mNode->SetInputBound(1, InputBound::INPUT_0_100); - mNode->SetInputBound(2, InputBound::INPUT_0_TO_1); - mNode->SetInputBound(3, InputBound::INPUT_0_100); - mNode->SetInputBound(4, InputBound::INPUT_0_TO_1); - mNode->SetInputBound(5, InputBound::INPUT_0_TO_1); - } - void GraphSignalSineFM2::Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) { - float* output0 = &outputs.at(0).GetOutput(numSamples); + float* output0 = &outputs.at(0).Get(numSamples); mSamplesUntilUpdate = l::audio::BatchUpdate(256.0f, mSamplesUntilUpdate, 0, numSamples, [&]() { @@ -383,21 +301,8 @@ namespace l::nodegraph { } /*********************************************************************/ - void GraphSignalSineFM3::Reset() { - // { "Note", "Volume", "Fmod", "Reset"} - mPhase = 0.0f; - mNode->SetInput(0, 0.0f); - mNode->SetInput(1, 0.5f); - mNode->SetInput(2, 0.5f); - mNode->SetInput(3, 0.0f); - mNode->SetInputBound(0, InputBound::INPUT_CUSTOM, 0.0f, l::math::constants::FLTMAX); - mNode->SetInputBound(1, InputBound::INPUT_0_100); - mNode->SetInputBound(2, InputBound::INPUT_0_TO_1); - mNode->SetInputBound(3, InputBound::INPUT_0_TO_1); - } - void GraphSignalSineFM3::Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) { - float* output0 = &outputs.at(0).GetOutput(numSamples); + float* output0 = &outputs.at(0).Get(numSamples); mSamplesUntilUpdate = l::audio::BatchUpdate(16.0f, mSamplesUntilUpdate, 0, numSamples, [&]() { @@ -444,25 +349,8 @@ namespace l::nodegraph { } /*********************************************************************/ - void GraphSignalSaw::Reset() { - // { "Freq", "Volume", "Fmod", "Phase", "Reset"}; - mPhase = 0.0f; - mNode->SetInput(0, 0.0f); - mNode->SetInput(1, 0.0f); - mNode->SetInput(2, 1.0f); - mNode->SetInput(3, 0.0f); - mNode->SetInput(4, 0.5f); - mNode->SetInput(5, 0.0f); - mNode->SetInputBound(0, InputBound::INPUT_CUSTOM, 0.0f, l::math::constants::FLTMAX); - mNode->SetInputBound(1, InputBound::INPUT_0_100); - mNode->SetInputBound(2, InputBound::INPUT_0_TO_2); - mNode->SetInputBound(3, InputBound::INPUT_0_TO_1); - mNode->SetInputBound(4, InputBound::INPUT_0_TO_1); - mNode->SetInputBound(5, InputBound::INPUT_0_TO_1); - } - void GraphSignalSaw::Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) { - float* output0 = &outputs.at(0).GetOutput(numSamples); + float* output0 = &outputs.at(0).Get(numSamples); mSamplesUntilUpdate = l::audio::BatchUpdate(256.0f, mSamplesUntilUpdate, 0, numSamples, [&]() { diff --git a/packages/nodegraph/source/common/operations/NodeGraphOpSource.cpp b/packages/nodegraph/source/common/operations/NodeGraphOpSource.cpp index 04539e65..75cf2887 100644 --- a/packages/nodegraph/source/common/operations/NodeGraphOpSource.cpp +++ b/packages/nodegraph/source/common/operations/NodeGraphOpSource.cpp @@ -12,31 +12,6 @@ namespace l::nodegraph { /* Mathematical operations */ /*********************************************************************/ - void GraphSourceConstants::Reset() { - switch (mMode) { - case 0: - for (int8_t i = 0; i < 4; i++) { - mNode->SetInputBound(i, InputBound::INPUT_0_TO_1); - } - break; - case 1: - for (int8_t i = 0; i < 4; i++) { - mNode->SetInputBound(i, InputBound::INPUT_NEG_1_POS_1); - } - break; - case 2: - for (int8_t i = 0; i < 4; i++) { - mNode->SetInputBound(i, InputBound::INPUT_0_100); - } - break; - default: - for (int8_t i = 0; i < 4; i++) { - mNode->SetInputBound(i, InputBound::INPUT_UNBOUNDED); - } - break; - } - } - void GraphSourceConstants::Process(int32_t, std::vector& inputs, std::vector& outputs) { for (int8_t i = 0; i < mNumOutputs; i++) { outputs.at(i).mOutput = inputs.at(i).Get(); @@ -48,22 +23,27 @@ namespace l::nodegraph { } /*********************************************************************/ - void GraphSourceTime::Process(int32_t, std::vector&, std::vector& outputs) { - float rate = 44100.0f; - float phaseChange = 1.0f / rate; - mAudioTime += phaseChange; + void GraphSourceTime::Process(int32_t, std::vector& inputs, std::vector& outputs) { + mAudioTick++; + + if (inputs.at(4).Get() > 0.5f) { + mAudioTick = 0; + mFrameTick = 0; + } - outputs.at(0).mOutput = mAudioTime; - outputs.at(1).mOutput = mFrameTime; + inputs.at(0).Get(1) = static_cast(mAudioTick); + inputs.at(1).Get(1) = static_cast(mFrameTick); + inputs.at(2).Get(1) = mAudioTick / static_cast(mAudioRate); + inputs.at(3).Get(1) = mFrameTick / static_cast(mFrameRate); + + for (int32_t i = 0; i < 4; i++) { + outputs.at(i).Get() = inputs.at(i).Get(); + } } void GraphSourceTime::Tick(int32_t, float deltaTime) { + mFrameTick++; mFrameTime += deltaTime; } - void GraphSourceTime::Reset() { - mAudioTime = 0.0f; - mFrameTime = 0.0f; - } - } diff --git a/packages/nodegraph/tests/common/NodeGraphSchemaTest.cpp b/packages/nodegraph/tests/common/NodeGraphSchemaTest.cpp index 85fdd67a..7c2b6132 100644 --- a/packages/nodegraph/tests/common/NodeGraphSchemaTest.cpp +++ b/packages/nodegraph/tests/common/NodeGraphSchemaTest.cpp @@ -98,18 +98,18 @@ TEST(NodeGraph, FilterLowpass) { float resonance = 0.9f; float input = 1.3f; - nodeLowpass.SetInput(0, &input); - nodeLowpass.SetInput(1, &cutoff); - nodeLowpass.SetInput(2, &resonance); + nodeLowpass.SetInput(1, &input); + nodeLowpass.SetInput(2, &cutoff); + nodeLowpass.SetInput(3, &resonance); float oneRev = 2.0f * 3.14f / 30.0f; for (int i = 0; i < 30; i++) { input = sinf(2.0f * i * oneRev); nodeLowpass.ProcessSubGraph(); - //LOG(LogInfo) << nodeLowpass.Get(0); + //LOG(LogInfo) << nodeLowpass.GetOutput(0); } - TEST_FUZZY(nodeLowpass.GetOutput(0), -0.448589f, 0.0001f, ""); + TEST_FUZZY(nodeLowpass.GetOutput(0), -0.005843f, 0.0001f, ""); return 0; } @@ -136,16 +136,16 @@ TEST(NodeGraph, GraphGroups) { auto nodeLowpass2 = group.NewNode(OutputType::Default); // left, right - nodeLowpass1->SetInput(0, group, 2); - nodeLowpass2->SetInput(0, group, 3); + nodeLowpass1->SetInput(1, group, 2); + nodeLowpass2->SetInput(1, group, 3); // cutoff - nodeLowpass1->SetInput(1, group, 0); - nodeLowpass2->SetInput(1, group, 0); + nodeLowpass1->SetInput(2, group, 0); + nodeLowpass2->SetInput(2, group, 0); // resonance - nodeLowpass1->SetInput(2, group, 1); - nodeLowpass2->SetInput(2, group, 1); + nodeLowpass1->SetInput(3, group, 1); + nodeLowpass2->SetInput(3, group, 1); group.SetOutput(0, *nodeLowpass1, 0); group.SetOutput(1, *nodeLowpass2, 0); @@ -159,9 +159,7 @@ TEST(NodeGraph, GraphGroups) { group2.SetInput(0, group, 0); group2.SetInput(1, group, 1); - auto copyNode = group2.NewNode(OutputType::Default); - copyNode->SetNumInputs(2); - copyNode->SetNumOutputs(2); + auto copyNode = group2.NewNode(OutputType::Default, 2); copyNode->SetInput(0, group2, 0); copyNode->SetInput(1, group2, 1); @@ -175,8 +173,8 @@ TEST(NodeGraph, GraphGroups) { float output1 = group2.GetOutput(0); float output2 = group2.GetOutput(1); - TEST_FUZZY(output1, 0.122880019f, 0.0000001, ""); - TEST_FUZZY(output2, 0.0819200128f, 0.0000001, ""); + TEST_FUZZY(output1, 0.00000537335563f, 0.00000001, ""); + TEST_FUZZY(output2, 0.00000358223701f, 0.00000001, ""); return 0; } diff --git a/packages/rendering/source/common/ui/UICreator.cpp b/packages/rendering/source/common/ui/UICreator.cpp index 4ab77f4a..6dd38d10 100644 --- a/packages/rendering/source/common/ui/UICreator.cpp +++ b/packages/rendering/source/common/ui/UICreator.cpp @@ -10,8 +10,7 @@ namespace l::ui { auto numInputChannels = node.GetNumInputs(); auto numOutputChannels = node.GetNumOutputs(); - auto numConstants = node.GetNumConstants(); - auto numRows = numInputChannels > numOutputChannels ? numInputChannels + numConstants : numOutputChannels; + auto numRows = numInputChannels > numOutputChannels ? numInputChannels : numOutputChannels; auto node4 = CreateSplit(uiStorage, l::ui::UIContainer_ResizeFlag | l::ui::UIContainer_MoveFlag | l::ui::UIContainer_DrawFlag | UIContainer_SelectFlag, l::ui::UIRenderType::RectFilled, l::ui::UISplitMode::AppendV); node4->SetColor(ImVec4(0.15f, 0.15f, 0.15f, 1.0f)); @@ -35,7 +34,7 @@ namespace l::ui { connector1Text->GetContainerArea().mRender.mColor = ImColor(ImVec4(0.5f, 1.0f, 0.4f, 1.0f)); row0->Add(connector1Text); } - for (int8_t i = 0; i < numInputChannels || i < numOutputChannels || i < numInputChannels + numConstants || i < numOutputChannels; i++) { + for (int8_t i = 0; i < numInputChannels || i < numOutputChannels || i < numInputChannels || i < numOutputChannels; i++) { auto row = CreateContainer(uiStorage, 0, l::ui::UIRenderType::Rect, l::ui::UIAlignH::Left, l::ui::UIAlignV::Top, l::ui::UILayoutH::Parent, l::ui::UILayoutV::Fixed); row->GetContainerArea().mMargin = ioSize; node4->Add(row); @@ -44,7 +43,7 @@ namespace l::ui { bool showsOutput = i < numOutputChannels; float estimatedWidth = 0.0f; - if (showsInput) { + if (showsInput && !node.IsDataConstant(i)) { auto in = CreateContainer(uiStorage, l::ui::UIContainer_InputFlag | l::ui::UIContainer_DrawFlag, l::ui::UIRenderType::CircleFilled, l::ui::UIAlignH::Left); in->SetPosition(ImVec2(-ioSize, ioSize * ioOffsetV)); in->SetSize(ImVec2(ioSize, ioSize)); diff --git a/packages/rendering/source/common/ui/UIVisitors.cpp b/packages/rendering/source/common/ui/UIVisitors.cpp index 992da034..1ca5d65e 100644 --- a/packages/rendering/source/common/ui/UIVisitors.cpp +++ b/packages/rendering/source/common/ui/UIVisitors.cpp @@ -211,7 +211,7 @@ namespace l::ui { auto nodeChannel = static_cast(container.GetChannelId()); if (node->IsDataEditable(nodeChannel)) { float* nodeValue = nullptr; - if (nodeChannel < node->GetNumInputs() + node->GetNumConstants()) { + if (nodeChannel < node->GetNumInputs()) { nodeValue = &node->GetInput(nodeChannel, 1); } else if (nodeChannel < node->GetNumOutputs()) { @@ -354,7 +354,7 @@ namespace l::ui { if (mNGSchema) { auto node = mNGSchema->GetNode(container.GetNodeId()); float nodeValue = 0.0f; - if (container.GetChannelId() < node->GetNumInputs() + node->GetNumConstants()) { + if (container.GetChannelId() < node->GetNumInputs()) { nodeValue = node->GetInput(static_cast(container.GetChannelId())); } else { From 83b8fecdff4b607c7a5e52c7039f54e0cdeaef26 Mon Sep 17 00:00:00 2001 From: lnd3 Date: Sat, 14 Sep 2024 15:58:18 +0200 Subject: [PATCH 18/42] Fix warnings. --- packages/nodegraph/include/nodegraph/core/NodeGraphBase.h | 2 +- packages/nodegraph/include/nodegraph/core/NodeGraphInput.h | 1 - packages/nodegraph/source/common/core/NodeGraphBase.cpp | 7 +++---- packages/nodegraph/source/common/core/NodeGraphInput.cpp | 4 +++- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/nodegraph/include/nodegraph/core/NodeGraphBase.h b/packages/nodegraph/include/nodegraph/core/NodeGraphBase.h index c1b0cb94..8b2d75c7 100644 --- a/packages/nodegraph/include/nodegraph/core/NodeGraphBase.h +++ b/packages/nodegraph/include/nodegraph/core/NodeGraphBase.h @@ -68,7 +68,7 @@ namespace l::nodegraph { virtual bool SetInput(int8_t inputChannel, float* floatPtr); virtual void SetDefaultOutput(int8_t outputChannel, float constant, int32_t size = 1); - virtual bool SetInputBound(int8_t inputChannel, InputBound bound = InputBound::INPUT_DONTCHANGE, float boundMin = 0.0f, float boundMax = 0.0f); + virtual bool SetInputBound(int8_t inputChannel, InputBound bound = InputBound::INPUT_0_TO_1, float boundMin = 0.0f, float boundMax = 1.0f); virtual bool DetachInput(void* source); virtual bool IsDataConstant(int8_t num); diff --git a/packages/nodegraph/include/nodegraph/core/NodeGraphInput.h b/packages/nodegraph/include/nodegraph/core/NodeGraphInput.h index 213365ef..dbb0b671 100644 --- a/packages/nodegraph/include/nodegraph/core/NodeGraphInput.h +++ b/packages/nodegraph/include/nodegraph/core/NodeGraphInput.h @@ -22,7 +22,6 @@ namespace l::nodegraph { }; enum class InputBound { - INPUT_DONTCHANGE = 0, INPUT_UNBOUNDED, INPUT_0_TO_1, INPUT_0_TO_2, diff --git a/packages/nodegraph/source/common/core/NodeGraphBase.cpp b/packages/nodegraph/source/common/core/NodeGraphBase.cpp index 740735f9..d0976fe8 100644 --- a/packages/nodegraph/source/common/core/NodeGraphBase.cpp +++ b/packages/nodegraph/source/common/core/NodeGraphBase.cpp @@ -207,8 +207,7 @@ namespace l::nodegraph { } auto& input = mInputs.at(inputChannel); - if (bound == InputBound::INPUT_DONTCHANGE) { - }else if (bound == InputBound::INPUT_CUSTOM) { + if (bound == InputBound::INPUT_CUSTOM) { input.mBoundMin = boundMin; input.mBoundMax = boundMax; } @@ -289,12 +288,12 @@ namespace l::nodegraph { } void NodeGraphOp::Reset() { - for (int8_t i = 0; i < mDefaultInData.size(); i++) { + for (int8_t i = 0; i < static_cast(mDefaultInData.size()); i++) { auto& e = mDefaultInData.at(i); mNode->SetInput(i, std::get<0>(e), std::get<1>(e)); mNode->SetInputBound(i, l::nodegraph::InputBound::INPUT_CUSTOM, std::get<2>(e), std::get<3>(e)); } - for (int8_t i = 0; i < mDefaultOutData.size(); i++) { + for (int8_t i = 0; i < static_cast(mDefaultOutData.size()); i++) { auto& e = mDefaultOutData.at(i); auto output = &mNode->GetOutput(i, std::get<1>(e)); for (int32_t j = 0; j < std::get<1>(e);j++) { diff --git a/packages/nodegraph/source/common/core/NodeGraphInput.cpp b/packages/nodegraph/source/common/core/NodeGraphInput.cpp index 80fa6eef..cfd2886d 100644 --- a/packages/nodegraph/source/common/core/NodeGraphInput.cpp +++ b/packages/nodegraph/source/common/core/NodeGraphInput.cpp @@ -19,8 +19,10 @@ namespace l::nodegraph { return { 0.0f, 100.0f }; case InputBound::INPUT_UNBOUNDED: return { -l::math::constants::FLTMAX, l::math::constants::FLTMAX }; + case InputBound::INPUT_CUSTOM: + return { 0.0f, 0.0f }; } - return { -l::math::constants::FLTMAX, l::math::constants::FLTMAX }; + return { 0.0f, 0.0f }; } void NodeGraphInput::Reset() { From 0a4ae2660cc57ee61baa4c6e320708a11b98a225 Mon Sep 17 00:00:00 2001 From: lnd3 Date: Sun, 15 Sep 2024 00:16:01 +0200 Subject: [PATCH 19/42] Begin integrating node batching. Add a input/output iterator to handle different buffer sizes transparently. Fix numerics, some of the signal generators and add effect base node for batching. Add node graph test for batching. --- packages/math/include/math/MathSmooth.h | 2 +- .../include/nodegraph/NodeGraphSchema.h | 19 +-- .../include/nodegraph/core/NodeGraphBase.h | 4 +- .../include/nodegraph/core/NodeGraphGroup.h | 5 +- .../include/nodegraph/core/NodeGraphInput.h | 26 +++- .../include/nodegraph/core/NodeGraphOutput.h | 23 ++++ .../nodegraph/operations/NodeGraphOpEffect.h | 97 +++++++++++++- .../nodegraph/operations/NodeGraphOpFilter.h | 2 +- .../nodegraph/operations/NodeGraphOpInput.h | 51 +++++--- .../nodegraph/operations/NodeGraphOpNumeric.h | 118 +++++++++++++++--- .../nodegraph/operations/NodeGraphOpOutput.h | 4 + .../nodegraph/operations/NodeGraphOpSignal.h | 2 +- .../source/common/NodeGraphSchema.cpp | 19 +-- .../source/common/core/NodeGraphBase.cpp | 16 +-- .../source/common/core/NodeGraphGroup.cpp | 20 ++- .../source/common/core/NodeGraphInput.cpp | 57 +++++---- .../source/common/core/NodeGraphOutput.cpp | 12 +- .../common/operations/NodeGraphOpEffect.cpp | 90 ++++++++++++- .../common/operations/NodeGraphOpOutput.cpp | 104 ++++++++------- .../common/operations/NodeGraphOpSource.cpp | 51 ++++++-- .../tests/common/NodeGraphBatchingTest.cpp | 49 ++++++++ .../tests/common/NodeGraphSchemaTest.cpp | 16 +++ 22 files changed, 614 insertions(+), 173 deletions(-) create mode 100644 packages/nodegraph/tests/common/NodeGraphBatchingTest.cpp diff --git a/packages/math/include/math/MathSmooth.h b/packages/math/include/math/MathSmooth.h index 2186f5a8..75be5fd4 100644 --- a/packages/math/include/math/MathSmooth.h +++ b/packages/math/include/math/MathSmooth.h @@ -33,7 +33,7 @@ namespace l::math::smooth { } template - V smootPolyh3(V x) { + V smoothPolyh3(V x) { V x2 = x * x; return static_cast(3.0) * x2 - static_cast(2.0) * x2 * x; } diff --git a/packages/nodegraph/include/nodegraph/NodeGraphSchema.h b/packages/nodegraph/include/nodegraph/NodeGraphSchema.h index d58dc5e3..e78eda07 100644 --- a/packages/nodegraph/include/nodegraph/NodeGraphSchema.h +++ b/packages/nodegraph/include/nodegraph/NodeGraphSchema.h @@ -63,12 +63,13 @@ namespace l::nodegraph { RegisterNodeType("Output", 202, "Plot"); 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"); - RegisterNodeType("Effect", 256, "Trance Gate"); - RegisterNodeType("Effect", 257, "Arpeggio"); + RegisterNodeType("Effect", 252, "Reverb2"); + //RegisterNodeType("Effect", 253, "Reverb2"); + RegisterNodeType("Effect", 254, "Limiter"); + RegisterNodeType("Effect", 255, "Envelope Follower"); + RegisterNodeType("Effect", 256, "Saturator"); + RegisterNodeType("Effect", 257, "Trance Gate"); + RegisterNodeType("Effect", 258, "Arpeggio"); RegisterNodeType("Input", 300, "Keyboard Piano"); RegisterNodeType("Input", 301, "Midi Keyboard"); RegisterNodeType("Input", 302, "Midi Knobs"); @@ -106,9 +107,9 @@ namespace l::nodegraph { NodeGraphGroup mMainNodeGraph; std::function mCreateCustomNode; - l::hid::KeyState* mKeyState; - l::audio::AudioStream* mAudioOutput; - l::hid::midi::MidiManager* mMidiManager; + l::hid::KeyState* mKeyState = nullptr; + l::audio::AudioStream* mAudioOutput = nullptr; + l::hid::midi::MidiManager* mMidiManager = nullptr; std::map> mRegisteredNodeTypes; }; diff --git a/packages/nodegraph/include/nodegraph/core/NodeGraphBase.h b/packages/nodegraph/include/nodegraph/core/NodeGraphBase.h index 8b2d75c7..66f34649 100644 --- a/packages/nodegraph/include/nodegraph/core/NodeGraphBase.h +++ b/packages/nodegraph/include/nodegraph/core/NodeGraphBase.h @@ -48,10 +48,10 @@ namespace l::nodegraph { virtual int8_t GetNumInputs(); virtual int8_t GetNumOutputs(); + virtual float& GetInput(int8_t inputChannel, int32_t size = 1); virtual float& GetOutput(int8_t outputChannel, int32_t size = 1); - virtual float GetInput(int8_t inputChannel); - virtual float& GetInput(int8_t inputChannel, int32_t size); + virtual int32_t GetInputSize(int8_t inputChannel); virtual int32_t GetOutputSize(int8_t outputChannel); virtual std::string_view GetName(); diff --git a/packages/nodegraph/include/nodegraph/core/NodeGraphGroup.h b/packages/nodegraph/include/nodegraph/core/NodeGraphGroup.h index 47f5af57..baa53250 100644 --- a/packages/nodegraph/include/nodegraph/core/NodeGraphGroup.h +++ b/packages/nodegraph/include/nodegraph/core/NodeGraphGroup.h @@ -49,7 +49,8 @@ namespace l::nodegraph { void SetOutput(int8_t outputChannel, NodeGraphBase& source, int8_t sourceOutputChannel); void SetOutput(int8_t outputChannel, NodeGraphGroup& source, int8_t sourceOutputChannel); - float GetOutput(int8_t outputChannel); + float& GetOutput(int8_t outputChannel, int32_t size = 1); + NodeGraphBase& GetInputNode(); NodeGraphBase& GetOutputNode(); @@ -69,7 +70,7 @@ namespace l::nodegraph { } void ClearProcessFlags(); - void ProcessSubGraph(int32_t numSamples, bool recomputeSubGraphCache = true); + void ProcessSubGraph(int32_t numSamples); void Tick(int32_t tickCount, float elapsed); protected: NodeGraphBase* mInputNode = nullptr; diff --git a/packages/nodegraph/include/nodegraph/core/NodeGraphInput.h b/packages/nodegraph/include/nodegraph/core/NodeGraphInput.h index dbb0b671..e48e3a3b 100644 --- a/packages/nodegraph/include/nodegraph/core/NodeGraphInput.h +++ b/packages/nodegraph/include/nodegraph/core/NodeGraphInput.h @@ -42,6 +42,28 @@ namespace l::nodegraph { int32_t mInputIntConstant; }; + class NodeInputDataIterator { + public: + NodeInputDataIterator(float* data, int32_t size) { + mData = data; + mSize = size; + mIncrement = size > 1 ? 1 : 0; + } + float& operator*() { + return *mData; + } + float* operator++(int) { + float* data = mData; + mData += mIncrement; + return data; + } + + protected: + float* mData = nullptr; + int32_t mSize = 0; + int32_t mIncrement = 0; + }; + struct NodeGraphInput { Input mInput; InputType mInputType = InputType::INPUT_EMPTY; @@ -57,8 +79,8 @@ namespace l::nodegraph { void Reset(); bool HasInputNode(); - float Get(); - float& Get(int32_t numSamples); + float& Get(int32_t numSamples = 1); + NodeInputDataIterator GetIterator(int32_t numSamples = 1); int32_t GetSize(); }; diff --git a/packages/nodegraph/include/nodegraph/core/NodeGraphOutput.h b/packages/nodegraph/include/nodegraph/core/NodeGraphOutput.h index 83a2f8ac..0ab413a7 100644 --- a/packages/nodegraph/include/nodegraph/core/NodeGraphOutput.h +++ b/packages/nodegraph/include/nodegraph/core/NodeGraphOutput.h @@ -19,6 +19,28 @@ namespace l::nodegraph { ExternalVisualOutput, }; + + class NodeOutputDataIterator { + public: + NodeOutputDataIterator(float* data, int32_t size) { + mData = data; + mSize = size; + mIncrement = size > 1 ? 1 : 0; + } + float& operator*() { + return *mData; + } + float* operator++(int) { + float* data = mData; + mData += mIncrement; + return data; + } + protected: + float* mData = nullptr; + int32_t mSize = 0; + int32_t mIncrement = 0; + }; + class NodeGraphOutput { public: NodeGraphOutput() = default; @@ -29,6 +51,7 @@ namespace l::nodegraph { bool mOutputPolled = false; float& Get(int32_t size = 1); + NodeOutputDataIterator GetIterator(int32_t numSamples = 1); int32_t GetSize(); bool IsPolled(); void ResetPollState(); diff --git a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpEffect.h b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpEffect.h index b25b592b..3571eedd 100644 --- a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpEffect.h +++ b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpEffect.h @@ -22,7 +22,94 @@ namespace l::nodegraph { + class GraphEffectBase : public NodeGraphOp { + public: + + static const int8_t mNumDefaultInputs = 6; + static const int8_t mNumDefaultOutputs = 2; + + GraphEffectBase(NodeGraphBase* node, std::string_view name) : + NodeGraphOp(node, name) + { + AddInput("Sync", 0.0f, 1, 0.0f, 1.0f); + AddInput("Rate", 256.0f, 1, 1.0f, 2048.0f); + AddInput("Gain", 0.5f, 1, 0.0f, 5.0f); + AddInput("Mix", 0.5f, 1, 0.0f, 1.0f); + AddInput("In 0"); + AddInput("In 1"); + + AddOutput("Out 1"); + AddOutput("Out 2"); + + mDeltaTime = 0.0f; + mSamplesUntilUpdate = 0.0f; + } + + virtual ~GraphEffectBase() = default; + virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override final; + virtual void UpdateSignal(std::vector&, std::vector&) {}; + virtual std::pair ProcessStereoSignal(float deltaTime, float value0, float value1) = 0; + protected: + float mUpdateRate = 256.0f; + + float mDeltaTime = 0.0f; + float mSamplesUntilUpdate = 0.0f; + + l::audio::FilterRWA mFilterGain; + l::audio::FilterRWA mFilterMix; + }; + /*********************************************************************/ + class GraphEffectReverb2 : public GraphEffectBase { + public: + uint32_t GetFramesPerRoomSize(float roomSize) { + const float metersPerFrame = 334.0f / 44100.0f; // (m/s)/(frames/s) = m/frames; + const float metersToWallPerFrame = metersPerFrame / 2.0f; // half the distance to wall for the bounced distance + const float framesPerRoom = roomSize / metersToWallPerFrame; + return static_cast(framesPerRoom); + } + + const float maxRoomSizeInMeters = 334.0f; // 334 meters large is 2 seconds of reverbation + + GraphEffectReverb2(NodeGraphBase* node) : + GraphEffectBase(node, "Reverb 3") { + + uint32_t bufferSize = GetFramesPerRoomSize(maxRoomSizeInMeters); + mBuf0.resize(bufferSize); + mBuf1.resize(bufferSize); + + AddInput("Attenuation", 0.5f, 1, 0.0f, 1.0f); + AddInput("Room Size", 30.0f, 1, 0.2f, maxRoomSizeInMeters); + AddInput("Delay 1", 0.5f, 1, 0.0f, 1.0f); + AddInput("Feedback 1", 0.9f, 1, 0.0f, 1.0f); + AddInput("Delay 2", 0.8f, 1, 0.0f, 1.0f); + AddInput("Feedback 2", 0.9f, 1, 0.0f, 1.0f); + AddInput("Delay 3", 0.7f, 1, 0.0f, 1.0f); + AddInput("Feedback 3", 0.9f, 1, 0.0f, 1.0f); + } + + virtual ~GraphEffectReverb2() = default; + virtual void UpdateSignal(std::vector&, std::vector&) override; + virtual std::pair ProcessStereoSignal(float deltaTime, float value0, float value1) override; + protected: + std::vector mBuf0; + std::vector mBuf1; + uint32_t mBufIndex = 0; + uint32_t mBufSizeLimit = 1; + uint32_t mDelay0 = 0; + uint32_t mDelay1 = 0; + uint32_t mDelay2 = 0; + + float fb = 0.0f; + float fb0 = 0.0f; + float fb1 = 0.0f; + float fb2 = 0.0f; + float d0 = 0.0f; + float d1 = 0.0f; + float d2 = 0.0f; + }; + /*********************************************************************/ + class GraphEffectReverb1 : public NodeGraphOp { public: uint32_t GetFramesPerRoomSize(float roomSize) { @@ -75,18 +162,18 @@ namespace l::nodegraph { }; /*********************************************************************/ - class GraphEffectReverb2 : public NodeGraphOp { + class GraphEffectReverb3 : public NodeGraphOp { public: uint32_t GetFramesPerRoomSize(float roomSize) { const float metersPerFrame = 334.0f / 44100.0f; // (m/s)/(frames/s) = m/frames; const float metersToWallPerFrame = metersPerFrame / 2.0f; // half the distance to wall for the bounced distance - const float framesPerRoom = roomSize / metersToWallPerFrame; + const float framesPerRoom = l::math::functions::min(5.0f, roomSize / metersToWallPerFrame); return static_cast(framesPerRoom); } const float maxRoomSizeInMeters = 334.0f; // 334 meters large is 2 seconds of reverbation - GraphEffectReverb2(NodeGraphBase* node) : + GraphEffectReverb3(NodeGraphBase* node) : NodeGraphOp(node, "Reverb 2") { uint32_t bufferSize = GetFramesPerRoomSize(maxRoomSizeInMeters); @@ -99,7 +186,7 @@ namespace l::nodegraph { AddInput("In 2"); AddInput("Mix", 0.3f, 1, 0.0f, 1.0f); AddInput("Feedback", 0.5f, 1, 0.0f, 1.0f); - AddInput("Room Size", 30.0f, 1, 0.2f, maxRoomSizeInMeters); + AddInput("Room Size", 30.0f, 1, 0.1f, maxRoomSizeInMeters); AddInput("Width", 0.5f, 1, 0.0f, 1.0f); AddInput("First tap", 0.1f, 1, 0.0f, 10.0f); AddInput("Longest tap", 0.8f, 1, 0.0f, 10.0f); @@ -114,7 +201,7 @@ namespace l::nodegraph { AddOutput("Tap 2"); } - virtual ~GraphEffectReverb2() = default; + virtual ~GraphEffectReverb3() = default; virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; protected: bool mInited = false; diff --git a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpFilter.h b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpFilter.h index c7b103d7..eb0e533c 100644 --- a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpFilter.h +++ b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpFilter.h @@ -38,7 +38,7 @@ namespace l::nodegraph { AddInput("Sync", 0.0f); AddInput("In"); - AddInput("Cutoff", 0.0f); + AddInput("Cutoff", 1.0f); AddInput("Resonance", 0.0f); AddOutput("Out", 0.0f); } diff --git a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpInput.h b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpInput.h index be1212b3..b4e2c079 100644 --- a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpInput.h +++ b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpInput.h @@ -84,14 +84,21 @@ namespace l::nodegraph { SetDevice(0); - mMidiManager->RegisterCallback([&](l::hid::midi::MidiData data) { - if (mMidiDeviceInId >= 0 && data.deviceIn == static_cast(mMidiDeviceInId)) { - MidiEvent(data); - } - }); + if (mMidiManager) { + mCallbackId = mMidiManager->RegisterCallback([&](l::hid::midi::MidiData data) { + if (mMidiDeviceInId >= 0 && data.deviceIn == static_cast(mMidiDeviceInId)) { + MidiEvent(data); + } + }); + } + } + + virtual ~GraphInputMidiKeyboard() { + if (mMidiManager != nullptr) { + mMidiManager->UnregisterCallback(mCallbackId); + } } - virtual ~GraphInputMidiKeyboard() = default; virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; virtual void Tick(int32_t tick, float deltaTime) override; @@ -105,7 +112,7 @@ namespace l::nodegraph { int8_t GetNextNoteChannel(int32_t note); void SetDevice(int32_t deviceId) { - if (deviceId >= 0 && mMidiDeviceInId != deviceId) { + if (mMidiManager != nullptr && deviceId >= 0 && mMidiDeviceInId != deviceId) { auto deviceInfo = mMidiManager->GetDeviceInfo(static_cast(deviceId)); if (deviceInfo) { mMidiDeviceInId = deviceId; @@ -117,6 +124,7 @@ namespace l::nodegraph { } l::hid::midi::MidiManager* mMidiManager = nullptr; + int32_t mCallbackId = 0; int32_t mLastNote = 0; int8_t mNoteCounter = 0; @@ -145,20 +153,27 @@ namespace l::nodegraph { SetDevice(0); - mMidiManager->RegisterCallback([&](l::hid::midi::MidiData data) { - if (mMidiDeviceInId >= 0 && data.deviceIn == static_cast(mMidiDeviceInId)) { - MidiEvent(data); - } - }); + if (mMidiManager != nullptr) { + mCallbackId = mMidiManager->RegisterCallback([&](l::hid::midi::MidiData data) { + if (mMidiDeviceInId >= 0 && data.deviceIn == static_cast(mMidiDeviceInId)) { + MidiEvent(data); + } + }); + } } - virtual ~GraphInputMidiKnobs() = default; + virtual ~GraphInputMidiKnobs() { + if (mMidiManager != nullptr) { + mMidiManager->UnregisterCallback(mCallbackId); + } + }; + virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; virtual void Tick(int32_t tick, float deltaTime) override; virtual void MidiEvent(const l::hid::midi::MidiData& data); protected: void SetDevice(int32_t deviceId) { - if (deviceId >= 0 && mMidiDeviceInId != deviceId) { + if (mMidiManager != nullptr && deviceId >= 0 && mMidiDeviceInId != deviceId) { auto deviceInfo = mMidiManager->GetDeviceInfo(static_cast(deviceId)); if (deviceInfo) { mMidiDeviceInId = deviceId; @@ -170,6 +185,8 @@ namespace l::nodegraph { } l::hid::midi::MidiManager* mMidiManager = nullptr; + int32_t mCallbackId = 0; + int32_t mMidiDeviceInId = -1; int32_t mMidiChannelKnobs = -1; }; @@ -249,7 +266,9 @@ namespace l::nodegraph { std::this_thread::yield(); - mMidiManager->UnregisterCallback(mCallbackId); + if (mMidiManager != nullptr) { + mMidiManager->UnregisterCallback(mCallbackId); + } } virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; virtual void Tick(int32_t tick, float deltaTime) override; @@ -277,7 +296,7 @@ namespace l::nodegraph { virtual void MidiEvent(const l::hid::midi::MidiData& data); protected: void SetDevice(int32_t deviceId) { - if (deviceId >= 0 && mMidiDeviceInId != deviceId) { + if (mMidiManager != nullptr && deviceId >= 0 && mMidiDeviceInId != deviceId) { auto deviceInfo = mMidiManager->GetDeviceInfo(static_cast(deviceId)); if (deviceInfo) { mMidiDeviceInId = deviceId; diff --git a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpNumeric.h b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpNumeric.h index 7388a1c9..41cd599a 100644 --- a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpNumeric.h +++ b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpNumeric.h @@ -33,8 +33,18 @@ namespace l::nodegraph { AddOutput("Out"); } virtual ~GraphNumericAdd() = default; - virtual void Process(int32_t, std::vector& inputs, std::vector& outputs) override { - outputs.at(0).mOutput = inputs.at(0).Get() + inputs.at(1).Get(); + virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override { + auto input0 = inputs.at(0).GetIterator(numSamples); + auto input1 = inputs.at(1).GetIterator(numSamples); + auto output = outputs.at(0).GetIterator(numSamples); + + if (numSamples > 1) { + ASSERT(numSamples == outputs.at(0).GetSize()); + } + + for (int32_t i = 0; i < numSamples; i++) { + *output++ = *input0++ + *input1++; + } } }; @@ -50,8 +60,18 @@ namespace l::nodegraph { } virtual ~GraphNumericMultiply() = default; - virtual void Process(int32_t, std::vector& inputs, std::vector& outputs) override { - outputs.at(0).mOutput = inputs.at(0).Get() * inputs.at(1).Get(); + virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override { + auto input0 = inputs.at(0).GetIterator(numSamples); + auto input1 = inputs.at(1).GetIterator(numSamples); + auto output = outputs.at(0).GetIterator(numSamples); + + if (numSamples > 1) { + ASSERT(numSamples == outputs.at(0).GetSize()); + } + + for (int32_t i = 0; i < numSamples; i++) { + *output++ = *input0++ * *input1++; + } } }; @@ -66,8 +86,18 @@ namespace l::nodegraph { AddOutput("Out"); } virtual ~GraphNumericSubtract() = default; - virtual void Process(int32_t, std::vector& inputs, std::vector& outputs) override { - outputs.at(0).mOutput = inputs.at(0).Get() - inputs.at(1).Get(); + virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override { + auto input0 = inputs.at(0).GetIterator(numSamples); + auto input1 = inputs.at(1).GetIterator(numSamples); + auto output = outputs.at(0).GetIterator(numSamples); + + if (numSamples > 1) { + ASSERT(numSamples == outputs.at(0).GetSize()); + } + + for (int32_t i = 0; i < numSamples; i++) { + *output++ = *input0++ - *input1++; + } } }; @@ -82,8 +112,17 @@ namespace l::nodegraph { } virtual ~GraphNumericNegate() = default; - virtual void Process(int32_t, std::vector& inputs, std::vector& outputs) override { - outputs.at(0).mOutput = -inputs.at(0).Get(); + virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override { + auto input0 = inputs.at(0).GetIterator(numSamples); + auto output = outputs.at(0).GetIterator(numSamples); + + if (numSamples > 1) { + ASSERT(numSamples == outputs.at(0).GetSize()); + } + + for (int32_t i = 0; i < numSamples; i++) { + *output++ = - *input0++; + } } }; @@ -93,14 +132,23 @@ namespace l::nodegraph { GraphNumericIntegral(NodeGraphBase* node) : NodeGraphOp(node, "Integral") { - AddInput("In"); - AddOutput("Out"); + AddInput("In", 0.0f, 1); + AddOutput("Out", 0.0f, 1); } virtual ~GraphNumericIntegral() = default; - virtual void Process(int32_t, std::vector& inputs, std::vector& outputs) override { - mOutput += inputs.at(0).Get(); - outputs.at(0).mOutput = mOutput; + virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override { + auto input0 = inputs.at(0).GetIterator(numSamples); + auto output = outputs.at(0).GetIterator(numSamples); + + if (numSamples > 1) { + ASSERT(numSamples == outputs.at(0).GetSize()); + } + + for (int32_t i = 0; i < numSamples; i++) { + mOutput += *input0++; + *output++ = mOutput; + } } protected: float mOutput = 0.0f; @@ -119,8 +167,19 @@ namespace l::nodegraph { } virtual ~GraphNumericMultiply3() = default; - virtual void Process(int32_t, std::vector& inputs, std::vector& outputs) override { - outputs.at(0).mOutput = inputs.at(0).Get() * inputs.at(1).Get() * inputs.at(2).Get(); + virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override { + auto input0 = inputs.at(0).GetIterator(numSamples); + auto input1 = inputs.at(1).GetIterator(numSamples); + auto input2 = inputs.at(2).GetIterator(numSamples); + auto output = outputs.at(0).GetIterator(numSamples); + + if (numSamples > 1) { + ASSERT(numSamples == outputs.at(0).GetSize()); + } + + for (int32_t i = 0; i < numSamples; i++) { + *output++ = *input0++ * *input1++ * *input2++; + } } }; @@ -137,8 +196,19 @@ namespace l::nodegraph { } virtual ~GraphNumericMultiplyAndAdd() = default; - void virtual Process(int32_t, std::vector& inputs, std::vector& outputs) override { - outputs.at(0).mOutput = inputs.at(0).Get() * inputs.at(1).Get() + inputs.at(2).Get(); + void virtual Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override { + auto input0 = inputs.at(0).GetIterator(numSamples); + auto input1 = inputs.at(1).GetIterator(numSamples); + auto input2 = inputs.at(2).GetIterator(numSamples); + auto output = outputs.at(0).GetIterator(numSamples); + + if (numSamples > 1) { + ASSERT(numSamples == outputs.at(0).GetSize()); + } + + for (int32_t i = 0; i < numSamples; i++) { + *output++ = *input0++ * *input1++ + *input2++; + } } }; @@ -151,9 +221,21 @@ namespace l::nodegraph { AddInput("In"); AddOutput("Out"); } + virtual ~GraphNumericRound() = default; - virtual void Process(int32_t, std::vector& inputs, std::vector& outputs) override { + virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override { outputs.at(0).mOutput = l::math::functions::round(inputs.at(0).Get()); + + auto input0 = inputs.at(2).GetIterator(numSamples); + auto output = outputs.at(0).GetIterator(numSamples); + + if (numSamples > 1) { + ASSERT(numSamples == outputs.at(0).GetSize()); + } + + for (int32_t i = 0; i < numSamples; i++) { + *output++ = l::math::functions::round(*input0++); + } } }; diff --git a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpOutput.h b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpOutput.h index 50032c8d..a74f45f4 100644 --- a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpOutput.h +++ b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpOutput.h @@ -61,8 +61,12 @@ namespace l::nodegraph { virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; protected: l::audio::AudioStream* mAudioStream; + float mSamplesUntilUpdate = 0.0f; + int32_t mCurrentStereoPosition; float mEnvelope = 0.0f; + float mAttack = 1.0f; + float mRelease = 1.0f; l::audio::FilterRWA mFilterEnvelope; }; diff --git a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpSignal.h b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpSignal.h index 7ee5ab84..45505a00 100644 --- a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpSignal.h +++ b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpSignal.h @@ -36,7 +36,7 @@ namespace l::nodegraph { AddInput("Freq", 0.0f, 1, 0.0f, 22050.0f); AddInput("Volume", 0.5f, 1, 0.0f, 5.0f); AddInput("Smooth", 1.0f, 1, 0.0f, 1.0f); - AddOutput("Out"); + AddOutput("Out", 0.0f, 2); mFreq = 0.0f; mVolumeTarget = 0.0f; diff --git a/packages/nodegraph/source/common/NodeGraphSchema.cpp b/packages/nodegraph/source/common/NodeGraphSchema.cpp index aec36dc9..bfcbd7ff 100644 --- a/packages/nodegraph/source/common/NodeGraphSchema.cpp +++ b/packages/nodegraph/source/common/NodeGraphSchema.cpp @@ -38,7 +38,7 @@ namespace l::nodegraph { node = mMainNodeGraph.NewNode(OutputType::Default, 3); break; case 4: - node = mMainNodeGraph.NewNode(OutputType::ExternalOutput, mAudioOutput->GetSampleRate(), 60); + node = mMainNodeGraph.NewNode(OutputType::ExternalOutput, mAudioOutput != nullptr ? mAudioOutput->GetSampleRate() : 44100, 60); break; case 50: node = mMainNodeGraph.NewNode(OutputType::Default); @@ -100,19 +100,19 @@ namespace l::nodegraph { case 252: node = mMainNodeGraph.NewNode(OutputType::Default); break; - case 253: + case 254: node = mMainNodeGraph.NewNode(OutputType::Default); break; - case 254: + case 255: node = mMainNodeGraph.NewNode(OutputType::Default); break; - case 255: + case 256: node = mMainNodeGraph.NewNode(OutputType::Default); break; - case 256: + case 257: node = mMainNodeGraph.NewNode(OutputType::Default); break; - case 257: + case 258: node = mMainNodeGraph.NewNode(OutputType::Default); break; case 300: @@ -162,8 +162,9 @@ namespace l::nodegraph { 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."; - node = mCreateCustomNode(typeId, mMainNodeGraph); + if (mCreateCustomNode) { + node = mCreateCustomNode(typeId, mMainNodeGraph); + } break; }; @@ -196,7 +197,7 @@ namespace l::nodegraph { } void NodeGraphSchema::ProcessSubGraph(int32_t numSamples) { - mMainNodeGraph.ProcessSubGraph(numSamples, true); + mMainNodeGraph.ProcessSubGraph(numSamples); } diff --git a/packages/nodegraph/source/common/core/NodeGraphBase.cpp b/packages/nodegraph/source/common/core/NodeGraphBase.cpp index d0976fe8..e2875f0c 100644 --- a/packages/nodegraph/source/common/core/NodeGraphBase.cpp +++ b/packages/nodegraph/source/common/core/NodeGraphBase.cpp @@ -80,20 +80,20 @@ namespace l::nodegraph { mLastTickCount = tickCount; } - float& NodeGraphBase::GetOutput(int8_t outputChannel, int32_t size) { - return mOutputs.at(outputChannel).Get(size); + float& NodeGraphBase::GetInput(int8_t inputChannel, int32_t size) { + return mInputs.at(inputChannel).Get(size); } - int32_t NodeGraphBase::GetOutputSize(int8_t outputChannel) { - return mOutputs.at(outputChannel).GetSize(); + float& NodeGraphBase::GetOutput(int8_t outputChannel, int32_t size) { + return mOutputs.at(outputChannel).Get(size); } - float NodeGraphBase::GetInput(int8_t inputChannel) { - return mInputs.at(inputChannel).Get(); + int32_t NodeGraphBase::GetInputSize(int8_t inputChannel) { + return mInputs.at(inputChannel).GetSize(); } - float& NodeGraphBase::GetInput(int8_t inputChannel, int32_t size) { - return mInputs.at(inputChannel).Get(size); + int32_t NodeGraphBase::GetOutputSize(int8_t outputChannel) { + return mOutputs.at(outputChannel).GetSize(); } bool NodeGraphBase::ClearInput(int8_t inputChannel) { diff --git a/packages/nodegraph/source/common/core/NodeGraphGroup.cpp b/packages/nodegraph/source/common/core/NodeGraphGroup.cpp index e11e401d..5a39135c 100644 --- a/packages/nodegraph/source/common/core/NodeGraphGroup.cpp +++ b/packages/nodegraph/source/common/core/NodeGraphGroup.cpp @@ -7,9 +7,18 @@ namespace l::nodegraph { - void GraphDataCopy::Process(int32_t, std::vector& inputs, std::vector& outputs) { + void GraphDataCopy::Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) { for (size_t i = 0; i < inputs.size() && i < outputs.size(); i++) { - outputs.at(i).mOutput = inputs.at(i).Get(); + auto numInputSamples = inputs.at(i).GetSize(); + if (numSamples > 1) { + ASSERT(numInputSamples == numSamples); + } + + auto input = &inputs.at(i).Get(numInputSamples); + auto output = &outputs.at(i).Get(numSamples); + for (int32_t j = 0; j < numSamples; j++) { + *output++ = *input++; + } } } @@ -22,7 +31,6 @@ namespace l::nodegraph { void NodeGraphGroup::SetNumOutputs(int8_t numOutput) { if (!mOutputNode) { mOutputNode = NewNode(OutputType::ExternalOutput, numOutput); - mOutputNodes.push_back(mOutputNode); } } @@ -52,8 +60,8 @@ namespace l::nodegraph { mOutputNode->SetOutputName(outputChannel, source.GetOutputNode().GetOutputName(sourceOutputChannel)); } - float NodeGraphGroup::GetOutput(int8_t outputChannel) { - return mOutputNode->GetOutput(outputChannel); + float& NodeGraphGroup::GetOutput(int8_t outputChannel, int32_t size) { + return mOutputNode->GetOutput(outputChannel, size); } NodeGraphBase& NodeGraphGroup::GetInputNode() { @@ -117,7 +125,7 @@ namespace l::nodegraph { mOutputNode->ClearProcessFlags(); } - void NodeGraphGroup::ProcessSubGraph(int32_t numSamples, bool) { + void NodeGraphGroup::ProcessSubGraph(int32_t numSamples) { for (auto& it : mOutputNodes) { it->ClearProcessFlags(); } diff --git a/packages/nodegraph/source/common/core/NodeGraphInput.cpp b/packages/nodegraph/source/common/core/NodeGraphInput.cpp index cfd2886d..285bfcfb 100644 --- a/packages/nodegraph/source/common/core/NodeGraphInput.cpp +++ b/packages/nodegraph/source/common/core/NodeGraphInput.cpp @@ -40,28 +40,6 @@ namespace l::nodegraph { return false; } - float NodeGraphInput::Get() { - float value = 0.0f; - switch (mInputType) { - case InputType::INPUT_NODE: - if (mInput.mInputNode != nullptr) { - value = mInput.mInputNode->GetOutput(mInputFromOutputChannel); - } - break; - case InputType::INPUT_CONSTANT: - value = mInput.mInputFloatConstant; - break; - case InputType::INPUT_ARRAY: - return *mInputBuf->data(); - case InputType::INPUT_VALUE: - value = *mInput.mInputFloat; - break; - case InputType::INPUT_EMPTY: - break; - } - return l::math::functions::clamp(value, mBoundMin, mBoundMax); - } - float& NodeGraphInput::Get(int32_t size) { switch (mInputType) { case InputType::INPUT_NODE: @@ -75,6 +53,9 @@ namespace l::nodegraph { } if (static_cast(mInputBuf->size()) < size) { mInputBuf->resize(size); + for (int32_t i = 0; i < mInputBuf->size();i++) { + (*mInputBuf)[i] = 0.0f; + } } return *mInputBuf->data(); case InputType::INPUT_CONSTANT: @@ -87,15 +68,33 @@ namespace l::nodegraph { return mInput.mInputFloatConstant; } - int32_t NodeGraphInput::GetSize() { - if (!mInputBuf) { - return 1; + NodeInputDataIterator NodeGraphInput::GetIterator(int32_t numSamples) { + auto size = GetSize(); + if (size > 1) { + ASSERT(size == numSamples); } - else { + return NodeInputDataIterator(&Get(numSamples), size); + } + + int32_t NodeGraphInput::GetSize() { + switch (mInputType) { + case InputType::INPUT_NODE: + if (mInput.mInputNode != nullptr) { + return mInput.mInputNode->GetOutputSize(mInputFromOutputChannel); + } + break; + case InputType::INPUT_ARRAY: + if (!mInputBuf) { + return 1; + } return static_cast(mInputBuf->size()); + case InputType::INPUT_CONSTANT: + return 1; + case InputType::INPUT_VALUE: + return 1; + case InputType::INPUT_EMPTY: + break; } + return 1; } - - - } \ No newline at end of file diff --git a/packages/nodegraph/source/common/core/NodeGraphOutput.cpp b/packages/nodegraph/source/common/core/NodeGraphOutput.cpp index c7e56a23..e11ba8c2 100644 --- a/packages/nodegraph/source/common/core/NodeGraphOutput.cpp +++ b/packages/nodegraph/source/common/core/NodeGraphOutput.cpp @@ -17,11 +17,22 @@ namespace l::nodegraph { } if (static_cast(mOutputBuf->size()) < size) { mOutputBuf->resize(size); + for (int32_t i = 0; i < mOutputBuf->size(); i++) { + (*mOutputBuf)[i] = 0.0f; + } } mOutputPolled = true; return *mOutputBuf->data(); } + NodeOutputDataIterator NodeGraphOutput::GetIterator(int32_t numSamples) { + auto size = GetSize(); + if (size > 1) { + ASSERT(size == numSamples); + } + return NodeOutputDataIterator(&Get(numSamples), size); + } + int32_t NodeGraphOutput::GetSize() { if (!mOutputBuf) { return 1; @@ -38,5 +49,4 @@ namespace l::nodegraph { void NodeGraphOutput::ResetPollState() { mOutputPolled = false; } - } \ No newline at end of file diff --git a/packages/nodegraph/source/common/operations/NodeGraphOpEffect.cpp b/packages/nodegraph/source/common/operations/NodeGraphOpEffect.cpp index d45ee7a3..703357c8 100644 --- a/packages/nodegraph/source/common/operations/NodeGraphOpEffect.cpp +++ b/packages/nodegraph/source/common/operations/NodeGraphOpEffect.cpp @@ -9,8 +9,86 @@ #include namespace l::nodegraph { + /*********************************************************************/ + + void GraphEffectBase::Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) { + float sync = inputs.at(0).Get(); + if (sync > 0.5f) { + mSamplesUntilUpdate = 0; + } + + auto input0 = inputs.at(4).GetIterator(numSamples); + auto input1 = inputs.at(5).GetIterator(numSamples); + auto output0 = outputs.at(0).GetIterator(numSamples); + auto output1 = outputs.at(1).GetIterator(numSamples); + + mFilterGain.SetConvergence(); + mFilterMix.SetConvergence(); + + mSamplesUntilUpdate = l::audio::BatchUpdate(mUpdateRate, mSamplesUntilUpdate, 0, numSamples, + [&]() { + mUpdateRate = inputs.at(1).Get(); + mFilterGain.SetTarget(inputs.at(2).Get()); + mFilterMix.SetTarget(inputs.at(3).Get()); + + mDeltaTime = 1.0f / 44100.0f; + + UpdateSignal(inputs, outputs); + }, + [&](int32_t start, int32_t end, bool) { + for (int32_t i = start; i < end; i++) { + float gain = mFilterGain.Next(); + float mix = gain * mFilterMix.Next(); + float antimix = gain * (1.0f - mix); + + auto in0 = *input0++; + auto in1 = *input1++; + auto [out0, out1] = ProcessStereoSignal(mDeltaTime, in0, in1); + *output0++ = mix * out0 + antimix * in0; + *output1++ = mix * out1 + antimix * in1; + } + } + ); + } + + /*********************************************************************/ + void GraphEffectReverb2::UpdateSignal(std::vector& inputs, std::vector&) { + fb = 0.2f * (1.0f - inputs.at(mNumDefaultInputs+0).Get()); + + float roomSize = inputs.at(mNumDefaultInputs+1).Get(); + mBufSizeLimit = GetFramesPerRoomSize(roomSize); + + d0 = inputs.at(mNumDefaultInputs+2).Get(); + fb0 = 0.5f * 0.5f * math::functions::max(inputs.at(mNumDefaultInputs+3).Get(), 1.0f); + d1 = inputs.at(mNumDefaultInputs+4).Get(); + fb1 = 0.5f * 0.5f * math::functions::max(inputs.at(mNumDefaultInputs+5).Get(), 1.0f); + d2 = inputs.at(mNumDefaultInputs+6).Get(); + fb2 = 0.5f * 0.5f * math::functions::max(inputs.at(mNumDefaultInputs+7).Get(), 1.0f); + + mDelay0 = (int(mBufIndex + d0 * mBufSizeLimit)) % mBufSizeLimit; + mDelay1 = (int(mBufIndex + d1 * mBufSizeLimit)) % mBufSizeLimit; + mDelay2 = (int(mBufIndex + d2 * mBufSizeLimit)) % mBufSizeLimit; + } + + std::pair GraphEffectReverb2::ProcessStereoSignal(float, float value0, float value1) { + float out0 = (fb1 * mBuf1[mDelay1] + fb0 * mBuf0[mDelay0] + fb2 * mBuf0[mDelay2]); + float out1 = (fb1 * mBuf0[mDelay1] + fb0 * mBuf1[mDelay0] + fb2 * mBuf1[mDelay2]); + + mBuf0[mBufIndex] = fb * mBuf1[mBufIndex] - fb1 * mBuf1[mDelay1] - fb0 * mBuf0[mDelay0] - fb2 * mBuf0[mDelay2] + value0; + mBuf1[mBufIndex] = fb * mBuf0[mBufIndex] - fb1 * mBuf0[mDelay1] - fb0 * mBuf1[mDelay0] - fb2 * mBuf1[mDelay2] + value1; + + mBufIndex = (mBufIndex + 1) % mBufSizeLimit; + + mDelay0 = (mDelay0 + 1) % mBufSizeLimit; + mDelay1 = (mDelay1 + 1) % mBufSizeLimit; + mDelay2 = (mDelay2 + 1) % mBufSizeLimit; + + return { out0, out1 }; + } + + /*********************************************************************/ - void GraphEffectReverb1::Process(int32_t numSamples, std::vector&inputs, std::vector&outputs) { + void GraphEffectReverb1::Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) { auto wet = &inputs.at(2).Get(numSamples); fb = 0.2f * (1.0f - inputs.at(3).Get()); @@ -48,7 +126,7 @@ namespace l::nodegraph { /*********************************************************************/ - void GraphEffectReverb2::Process(int32_t, std::vector& inputs, std::vector& outputs) { + void GraphEffectReverb3::Process(int32_t, std::vector& inputs, std::vector& outputs) { float wet = inputs.at(2).Get(); float reverbFeedback = inputs.at(3).Get(); float roomSize = inputs.at(4).Get(); @@ -366,10 +444,10 @@ namespace l::nodegraph { float delta = mGainTarget - mGain; if (delta > 0) { - mGain += mGateSmoothing * l::math::smooth::smootPolyh3(delta); + mGain += mGateSmoothing * l::math::smooth::smoothPolyh3(delta); } else { - mGain += mGateSmoothingNeg * (-l::math::smooth::smootPolyh3(-delta)); + mGain += mGateSmoothingNeg * (-l::math::smooth::smoothPolyh3(-delta)); } outputs.at(0).mOutput = mGain * in0; @@ -444,10 +522,10 @@ namespace l::nodegraph { for (int32_t i = start; i < end; i++) { float delta = mGainTarget - mGain; if (delta > 0) { - mGain += mGainSmoothing * l::math::smooth::smootPolyh3(delta); + mGain += mGainSmoothing * l::math::smooth::smoothPolyh3(delta); } else { - mGain += mGainSmoothingNeg * (-l::math::smooth::smootPolyh3(-delta)); + mGain += mGainSmoothingNeg * (-l::math::smooth::smoothPolyh3(-delta)); } mFreq += mFreqSmoothing * (mFreqTarget - mFreq); diff --git a/packages/nodegraph/source/common/operations/NodeGraphOpOutput.cpp b/packages/nodegraph/source/common/operations/NodeGraphOpOutput.cpp index 956982e1..af7ef5b4 100644 --- a/packages/nodegraph/source/common/operations/NodeGraphOpOutput.cpp +++ b/packages/nodegraph/source/common/operations/NodeGraphOpOutput.cpp @@ -18,55 +18,65 @@ namespace l::nodegraph { } /*********************************************************************/ - void GraphOutputSpeaker::Process(int32_t, std::vector& inputs, std::vector&) { - auto& buffer = mAudioStream->GetWriteBuffer(); - - float in0 = inputs.at(0).Get(); - float in1 = inputs.at(1).Get(); - - float volume = inputs.at(2).Get(); - mFilterEnvelope.SetTarget(volume * volume).SnapAt(); - - float attackMs = 40.0f; - float releaseMs = 40.0f; - 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 limit = mFilterEnvelope.Next(); - float limitedVolume = 0.95f * limit; - - float inVal0 = in0 * limitedVolume; - float inVal1 = in1 * limitedVolume; - float inVal = inVal0 > inVal1 ? inVal0 : inVal1; - if (inVal > mEnvelope) { - mEnvelope = attack * (mEnvelope - inVal) + inVal; - } - else { - mEnvelope = release * (mEnvelope - inVal) + inVal; - } + void GraphOutputSpeaker::Process(int32_t numSamples, std::vector& inputs, std::vector&) { - float out0 = 0.0f; - float out1 = 0.0f; - float envelopeAbs = l::math::functions::abs(mEnvelope); - if (envelopeAbs > limit) { - if (envelopeAbs > 1.0f) { - out0 = inVal0 / mEnvelope; - out1 = inVal1 / mEnvelope; - } - else { - out0 = inVal0 / (1.0f + mEnvelope - limit); - out1 = inVal1 / (1.0f + mEnvelope - limit); + auto& buffer = mAudioStream->GetWriteBuffer(); + auto in0 = inputs.at(0).GetIterator(numSamples); + auto in1 = inputs.at(1).GetIterator(numSamples); + + mSamplesUntilUpdate = l::audio::BatchUpdate(256.0f, mSamplesUntilUpdate, 0, numSamples, + [&]() { + auto volume = inputs.at(2).Get(); + mFilterEnvelope.SetTarget(volume * volume).SnapAt(); + + float attackMs = 40.0f; + float releaseMs = 40.0f; + mAttack = l::math::functions::pow(0.001f, 1.0f / (attackMs * 44100.0f * 0.001f)); + mRelease = l::math::functions::pow(0.001f, 1.0f / (releaseMs * 44100.0f * 0.001f)); + mAttack = 0.01f; + mRelease = 0.01f; + }, + [&](int32_t start, int32_t end, bool) { + + for (int32_t i = start; i < end; i++) { + float limit = mFilterEnvelope.Next(); + float limitedVolume = 0.95f * limit; + + float inVal0 = (*in0++) * limitedVolume; + float inVal1 = (*in1++) * limitedVolume; + float inVal = inVal0 > inVal1 ? inVal0 : inVal1; + if (inVal > mEnvelope) { + mEnvelope = mAttack * (mEnvelope - inVal) + inVal; + } + else { + mEnvelope = mRelease * (mEnvelope - inVal) + inVal; + } + + float out0 = 0.0f; + float out1 = 0.0f; + float envelopeAbs = l::math::functions::abs(mEnvelope); + if (envelopeAbs > limit) { + if (envelopeAbs > 1.0f) { + out0 = inVal0 / mEnvelope; + out1 = inVal1 / mEnvelope; + } + else { + out0 = inVal0 / (1.0f + mEnvelope - limit); + out1 = inVal1 / (1.0f + mEnvelope - limit); + } + } + else { + out0 = inVal0; + out1 = inVal1; + } + + buffer[mCurrentStereoPosition++] = out0; + buffer[mCurrentStereoPosition++] = out1; + + mCurrentStereoPosition %= mAudioStream->GetPartTotalSize(); + } } - } - else { - out0 = inVal0; - out1 = inVal1; - } - - buffer[mCurrentStereoPosition++] = out0; - buffer[mCurrentStereoPosition++] = out1; - - mCurrentStereoPosition %= mAudioStream->GetPartTotalSize(); + ); } /*********************************************************************/ diff --git a/packages/nodegraph/source/common/operations/NodeGraphOpSource.cpp b/packages/nodegraph/source/common/operations/NodeGraphOpSource.cpp index 75cf2887..f97329e9 100644 --- a/packages/nodegraph/source/common/operations/NodeGraphOpSource.cpp +++ b/packages/nodegraph/source/common/operations/NodeGraphOpSource.cpp @@ -12,9 +12,12 @@ namespace l::nodegraph { /* Mathematical operations */ /*********************************************************************/ - void GraphSourceConstants::Process(int32_t, std::vector& inputs, std::vector& outputs) { + void GraphSourceConstants::Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) { + auto input0 = inputs.at(0).GetIterator(numSamples); + auto output0 = outputs.at(0).GetIterator(numSamples); + for (int8_t i = 0; i < mNumOutputs; i++) { - outputs.at(i).mOutput = inputs.at(i).Get(); + *output0++ = *input0++; } } @@ -23,21 +26,49 @@ namespace l::nodegraph { } /*********************************************************************/ - void GraphSourceTime::Process(int32_t, std::vector& inputs, std::vector& outputs) { - mAudioTick++; + void GraphSourceTime::Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) { + + auto input0 = inputs.at(0).GetIterator(numSamples); + auto input1 = inputs.at(1).GetIterator(numSamples); + auto input2 = inputs.at(2).GetIterator(numSamples); + auto input3 = inputs.at(3).GetIterator(numSamples); + auto output0 = outputs.at(0).GetIterator(numSamples); + auto output1 = outputs.at(1).GetIterator(numSamples); + auto output2 = outputs.at(2).GetIterator(numSamples); + auto output3 = outputs.at(03).GetIterator(numSamples); if (inputs.at(4).Get() > 0.5f) { mAudioTick = 0; mFrameTick = 0; + + for (int32_t i = 0; i < numSamples; i++) { + mAudioTick++; + + *input0 = static_cast(mAudioTick); + *input1 = static_cast(mFrameTick); + *input2 = mAudioTick / static_cast(mAudioRate); + *input3 = mFrameTick / static_cast(mFrameRate); + + *output0++ = 0.0f; + *output1++ = 0.0f; + *output2++ = 0.0f; + *output3++ = 0.0f; + } } + else { + for (int32_t i = 0; i < numSamples; i++) { + mAudioTick++; - inputs.at(0).Get(1) = static_cast(mAudioTick); - inputs.at(1).Get(1) = static_cast(mFrameTick); - inputs.at(2).Get(1) = mAudioTick / static_cast(mAudioRate); - inputs.at(3).Get(1) = mFrameTick / static_cast(mFrameRate); + *input0 = static_cast(mAudioTick); + *input1 = static_cast(mFrameTick); + *input2 = mAudioTick / static_cast(mAudioRate); + *input3 = mFrameTick / static_cast(mFrameRate); - for (int32_t i = 0; i < 4; i++) { - outputs.at(i).Get() = inputs.at(i).Get(); + *output0++ = *input0++; + *output1++ = *input1++; + *output2++ = *input2++; + *output3++ = *input3++; + } } } diff --git a/packages/nodegraph/tests/common/NodeGraphBatchingTest.cpp b/packages/nodegraph/tests/common/NodeGraphBatchingTest.cpp new file mode 100644 index 00000000..46edf4a7 --- /dev/null +++ b/packages/nodegraph/tests/common/NodeGraphBatchingTest.cpp @@ -0,0 +1,49 @@ +#include "testing/Test.h" +#include "logging/Log.h" + +#include "nodegraph/core/NodeGraphGroup.h" +#include "nodegraph/NodeGraphSchema.h" + +using namespace l; +using namespace l::nodegraph; + +TEST(NodeGraphBatching, Simple) { + + NodeGraphGroup group; + group.SetNumOutputs(1); + + auto nodeSine = group.NewNode(OutputType::Default); + nodeSine->SetInput(1, 4.0f); // sine update rate + nodeSine->SetInput(2, 1400.0f); // sine freq + + auto nodeLowpass = group.NewNode(OutputType::Default); + + nodeLowpass->SetInput(1, *nodeSine, 0); + group.SetOutput(0, *nodeLowpass, 0); + + + for (int32_t i = 0; i < 4; i++) { + LOG(LogInfo) << "batch " << i; + + group.ProcessSubGraph(8); // 8 samples per batch + auto output = &group.GetOutput(0, 8); + + for (int32_t j = 0; j < 8; j++) { + LOG(LogInfo) << "sample " << (j+(i*8)) << ": " << *output++; + } + } + + auto output = &group.GetOutput(0, 8); + + TEST_FUZZY(0.000151574f, output[7], 0.0001f, ""); + + return 0; +} + + +TEST(NodeGraphBatching, Multiple) { + + return 0; +} + + diff --git a/packages/nodegraph/tests/common/NodeGraphSchemaTest.cpp b/packages/nodegraph/tests/common/NodeGraphSchemaTest.cpp index 7c2b6132..a317b235 100644 --- a/packages/nodegraph/tests/common/NodeGraphSchemaTest.cpp +++ b/packages/nodegraph/tests/common/NodeGraphSchemaTest.cpp @@ -181,6 +181,22 @@ TEST(NodeGraph, GraphGroups) { TEST(NodeGraph, SchemaBasic) { + NodeGraphSchema ng; + + std::vector nodeIds; + + for (int32_t i = 0; i < 1000; i++) { + auto nodeId = ng.NewNode(i); + if (nodeId > 0) { + nodeIds.push_back(nodeId); + } + } + + for (auto nodeId : nodeIds) { + TEST_TRUE(ng.RemoveNode(nodeId), ""); + } + + return 0; } From 33d04c1401296ed7b955ee8992c1eb7fe3c326a5 Mon Sep 17 00:00:00 2001 From: lnd3 Date: Mon, 16 Sep 2024 21:20:05 +0200 Subject: [PATCH 20/42] Generalize node input data as either constants, batch buffers, custom sized buffers or interpolated parameters. Make some integrations. Improved node graph test so it smoke tests all existing node operations. --- packages/audio/include/audio/AudioUtils.h | 18 +- packages/audio/source/common/AudioUtils.cpp | 10 +- .../include/nodegraph/NodeGraphSchema.h | 5 +- .../include/nodegraph/core/NodeGraphBase.h | 77 +++++- .../include/nodegraph/core/NodeGraphInput.h | 198 +++++++++++++++- .../nodegraph/operations/NodeGraphOpControl.h | 134 +++++++++++ .../nodegraph/operations/NodeGraphOpEffect.h | 103 ++------ .../nodegraph/operations/NodeGraphOpInput.h | 6 +- .../nodegraph/operations/NodeGraphOpNumeric.h | 30 +-- .../nodegraph/operations/NodeGraphOpOutput.h | 2 +- .../nodegraph/operations/NodeGraphOpSource.h | 8 +- .../source/common/NodeGraphSchema.cpp | 21 +- .../source/common/core/NodeGraphBase.cpp | 24 +- .../source/common/core/NodeGraphInput.cpp | 9 +- .../source/common/core/NodeGraphOutput.cpp | 2 +- .../common/operations/NodeGraphOpControl.cpp | 214 +++++++++++++++++ .../common/operations/NodeGraphOpEffect.cpp | 220 +++--------------- .../common/operations/NodeGraphOpOutput.cpp | 7 +- .../common/operations/NodeGraphOpSource.cpp | 10 +- .../tests/common/NodeGraphSchemaTest.cpp | 8 + 20 files changed, 781 insertions(+), 325 deletions(-) create mode 100644 packages/nodegraph/include/nodegraph/operations/NodeGraphOpControl.h create mode 100644 packages/nodegraph/source/common/operations/NodeGraphOpControl.cpp diff --git a/packages/audio/include/audio/AudioUtils.h b/packages/audio/include/audio/AudioUtils.h index 2324718a..ca9b13a6 100644 --- a/packages/audio/include/audio/AudioUtils.h +++ b/packages/audio/include/audio/AudioUtils.h @@ -19,7 +19,10 @@ namespace l::audio { extern const int32_t gNoNote; float GetFrequencyFromNote(float note); double GetPhaseModifier(double note, double modifier); - float GetRCAFactor(float numSamples, float limit = 0.01f); + + float GetRWAFactorFromMS(float numMS, float limit, float rate = 44100.0f); + float GetRWAFactorFromTicks(float numTicks, float limit); + float BatchUpdate(float updateSamples, float samplesLeft, int32_t start, int32_t end, std::function update, std::function process); template @@ -31,7 +34,7 @@ namespace l::audio { mValue(static_cast(0)), mTargetValue(static_cast(0)) {} - ~FilterRWA() {} + ~FilterRWA() = default; bool SnapAt(T value = static_cast(0), T proximity = static_cast(0.00001)) { if (l::math::functions::abs(mValue - value) < proximity) { @@ -41,6 +44,11 @@ namespace l::audio { return false; } + FilterRWA& SetConvergenceInMs(T convergenceInMS, T limit = static_cast(0.0001)) { + mSmooth = GetRWAFactorFromMS(convergenceInMS, limit); + return *this; + } + FilterRWA& SetConvergence(T smooth = static_cast(0.005)) { mSmooth = smooth; return *this; @@ -56,9 +64,15 @@ namespace l::audio { return mValue; } + T& Value() { + return mValue; + } + protected: T mSmooth; T mValue; T mTargetValue; }; + + using FilterRWAFloat = FilterRWA; } diff --git a/packages/audio/source/common/AudioUtils.cpp b/packages/audio/source/common/AudioUtils.cpp index 13fa3701..6f9e13c6 100644 --- a/packages/audio/source/common/AudioUtils.cpp +++ b/packages/audio/source/common/AudioUtils.cpp @@ -20,8 +20,14 @@ namespace l::audio { return 800.0 * modifier * modifier * limit; } - float GetRCAFactor(float numSamples, float limit) { - return limit / l::math::functions::log(static_cast(numSamples)); + float GetRWAFactorFromMS(float ms, float limit, float rate) { + float ticks = ms * rate / 1000.0f; + return GetRWAFactorFromTicks(ticks, limit); + } + + float GetRWAFactorFromTicks(float numTicks, float limit) { + // rwa factor = pow(e, log(limit)/ticks) + return 1.0f - l::math::functions::pow(l::math::constants::E_f, l::math::functions::log(limit) / numTicks); } float BatchUpdate(float updateSamples, float samplesLeft, int32_t start, int32_t end, std::function update, std::function process) { diff --git a/packages/nodegraph/include/nodegraph/NodeGraphSchema.h b/packages/nodegraph/include/nodegraph/NodeGraphSchema.h index e78eda07..ab6f89ba 100644 --- a/packages/nodegraph/include/nodegraph/NodeGraphSchema.h +++ b/packages/nodegraph/include/nodegraph/NodeGraphSchema.h @@ -3,6 +3,7 @@ #include "logging/LoggingAll.h" #include "nodegraph/core/NodeGraphGroup.h" +#include "nodegraph/operations/NodeGraphOpControl.h" #include "nodegraph/operations/NodeGraphOpEffect.h" #include "nodegraph/operations/NodeGraphOpFilter.h" #include "nodegraph/operations/NodeGraphOpInput.h" @@ -69,7 +70,6 @@ namespace l::nodegraph { RegisterNodeType("Effect", 255, "Envelope Follower"); RegisterNodeType("Effect", 256, "Saturator"); RegisterNodeType("Effect", 257, "Trance Gate"); - RegisterNodeType("Effect", 258, "Arpeggio"); RegisterNodeType("Input", 300, "Keyboard Piano"); RegisterNodeType("Input", 301, "Midi Keyboard"); RegisterNodeType("Input", 302, "Midi Knobs"); @@ -85,6 +85,9 @@ namespace l::nodegraph { RegisterNodeType("Signal", 354, "Saw"); RegisterNodeType("Signal", 355, "Sine 2"); RegisterNodeType("Signal", 356, "Saw 2"); + RegisterNodeType("Control", 400, "Envelope"); + RegisterNodeType("Control", 401, "Arpeggio"); + } diff --git a/packages/nodegraph/include/nodegraph/core/NodeGraphBase.h b/packages/nodegraph/include/nodegraph/core/NodeGraphBase.h index 66f34649..325d32dc 100644 --- a/packages/nodegraph/include/nodegraph/core/NodeGraphBase.h +++ b/packages/nodegraph/include/nodegraph/core/NodeGraphBase.h @@ -118,11 +118,12 @@ namespace l::nodegraph { virtual std::string_view GetInputName(int8_t inputChannel); virtual std::string_view GetOutputName(int8_t outputChannel); virtual std::string_view GetName(); + virtual float GetDefaultData(int8_t inputChannel); protected: - virtual void AddInput(std::string_view name, float defaultValue = 0.0f, int32_t size = 1, float boundMin = -l::math::constants::FLTMAX, float boundMax = l::math::constants::FLTMAX, bool visible = true, bool editable = true); - virtual void AddOutput(std::string_view name, float defaultValue = 0.0f, int32_t size = 1); - virtual void AddConstant(std::string_view name, float defaultValue = 0.0f, float boundMin = -l::math::constants::FLTMAX, float boundMax = l::math::constants::FLTMAX, bool visible = true, bool editable = true); + virtual int32_t AddInput(std::string_view name, float defaultValue = 0.0f, int32_t size = 1, float boundMin = -l::math::constants::FLTMAX, float boundMax = l::math::constants::FLTMAX, bool visible = true, bool editable = true); + virtual int32_t AddOutput(std::string_view name, float defaultValue = 0.0f, int32_t size = 1); + virtual int32_t AddConstant(std::string_view name, float defaultValue = 0.0f, int32_t size = 1, float boundMin = -l::math::constants::FLTMAX, float boundMax = l::math::constants::FLTMAX, bool visible = true, bool editable = true); NodeGraphBase* mNode; std::string mName; @@ -213,5 +214,75 @@ namespace l::nodegraph { T mOperation; }; + + + class NodeInputManager { + public: + NodeInputManager(NodeGraphOp& nodeGraphOperation) : + mNodeGraphOperation(nodeGraphOperation) + { + } + ~NodeInputManager() = default; + + int32_t AddInputBase(InputTypeBase type, int32_t inputIndex = -1) { + if (type != InputTypeBase::CUSTOM_VALUE_INTERP_MS) { + ASSERT(inputIndex >= 0); + } + + inputIndex = static_cast(mInputs.size()); + mInputs.emplace_back(NodeInput{ type, inputIndex }); + + if (type != InputTypeBase::CUSTOM_VALUE_INTERP_MS) { + mInputs.back().SetValue(mNodeGraphOperation.GetDefaultData(static_cast(inputIndex))); + mInputs.back().SetTarget(mNodeGraphOperation.GetDefaultData(static_cast(inputIndex))); + } + return inputIndex; + } + + void ProcessUpdate(std::vector& inputs, int32_t numSamples) { + for (auto& input : mInputs) { + input.ProcessUpdate(inputs, numSamples); + } + } + + void NodeUpdate(std::vector& inputs) { + for (auto& input : mInputs) { + input.NodeUpdate(inputs); + } + } + + float GetValueNext(int32_t inputIndex) { + return mInputs.at(inputIndex).GetValueNext(); + } + + float GetValue(int32_t inputIndex) { + return mInputs.at(inputIndex).GetValue(); + } + + float GetArrayValue(int32_t inputIndex, int32_t arrayIndex) { + return mInputs.at(inputIndex).GetArrayValue(arrayIndex); + } + + float* GetArray(int32_t inputIndex) { + return mInputs.at(inputIndex).GetArray(); + } + + void SetConvergence(int32_t inputIndex, float value) { + mInputs.at(inputIndex).SetConvergence(value); + } + + void SetTarget(int32_t inputIndex, float value) { + mInputs.at(inputIndex).SetTarget(value); + } + + void SetValue(int32_t inputIndex, float value) { + mInputs.at(inputIndex).SetValue(value); + } + + protected: + NodeGraphOp& mNodeGraphOperation; + std::vector mInputs; + //std::vector mInputsRefs; + }; } diff --git a/packages/nodegraph/include/nodegraph/core/NodeGraphInput.h b/packages/nodegraph/include/nodegraph/core/NodeGraphInput.h index e48e3a3b..8214ded7 100644 --- a/packages/nodegraph/include/nodegraph/core/NodeGraphInput.h +++ b/packages/nodegraph/include/nodegraph/core/NodeGraphInput.h @@ -10,6 +10,7 @@ #include #include "math/MathConstants.h" +#include "audio/AudioUtils.h" namespace l::nodegraph { @@ -38,17 +39,17 @@ namespace l::nodegraph { NodeGraphBase* mInputNode = nullptr; float* mInputFloat; float mInputFloatConstant; - int32_t* mInputInt; - int32_t mInputIntConstant; }; class NodeInputDataIterator { public: - NodeInputDataIterator(float* data, int32_t size) { + NodeInputDataIterator(float* data = nullptr, int32_t size = 0) { mData = data; mSize = size; mIncrement = size > 1 ? 1 : 0; } + ~NodeInputDataIterator() = default; + float& operator*() { return *mData; } @@ -57,6 +58,17 @@ namespace l::nodegraph { mData += mIncrement; return data; } + float operator[](int index) { + return mData[index]; + } + float* data() { + return mData; + } + void Reset(float* data, int32_t size) { + mData = data; + mSize = size; + mIncrement = size > 1 ? 1 : 0; + } protected: float* mData = nullptr; @@ -64,7 +76,8 @@ namespace l::nodegraph { int32_t mIncrement = 0; }; - struct NodeGraphInput { + class NodeGraphInput { + public: Input mInput; InputType mInputType = InputType::INPUT_EMPTY; @@ -72,7 +85,7 @@ namespace l::nodegraph { float mBoundMax = l::math::constants::FLTMAX; int8_t mInputFromOutputChannel = 0; - std::unique_ptr mName; + std::unique_ptr mName = nullptr; // hack to get input buffers working std::unique_ptr> mInputBuf = nullptr; @@ -80,9 +93,182 @@ namespace l::nodegraph { void Reset(); bool HasInputNode(); float& Get(int32_t numSamples = 1); - NodeInputDataIterator GetIterator(int32_t numSamples = 1); + NodeInputDataIterator GetBufferIterator(int32_t numSamples = 1); + NodeInputDataIterator GetArrayIterator(); int32_t GetSize(); }; + + /****************************************************************************************/ + enum class InputTypeBase { + SAMPLED = 0, + INTERPOLATED, + INTERPOLATED_MS, + CONSTANT_VALUE, + CONSTANT_ARRAY, + CUSTOM_VALUE_INTERP_MS + }; + + union InputUnion { + l::audio::FilterRWAFloat mFilter; + NodeInputDataIterator mIterator; + + InputUnion() : mFilter() {} + ~InputUnion() = default; + }; + + class NodeInput { + public: + NodeInput(InputTypeBase type, int32_t inputIndex) : + mType(type), + mInputIndex(inputIndex) + { + switch(mType) { + case InputTypeBase::INTERPOLATED: + case InputTypeBase::INTERPOLATED_MS: + case InputTypeBase::CUSTOM_VALUE_INTERP_MS: + //new (&s.vec) std::vector; + new (&mInput.mFilter) l::audio::FilterRWA(); + + //mInput.mFilter = l::audio::FilterRWA(); + break; + case InputTypeBase::SAMPLED: + case InputTypeBase::CONSTANT_VALUE: + case InputTypeBase::CONSTANT_ARRAY: + new (&mInput.mIterator) NodeInputDataIterator(); + //mInput.mIterator = NodeInputDataIterator(); + break; + } + } + ~NodeInput() = default; + + void SetConvergence(float value) { + switch(mType) { + case InputTypeBase::INTERPOLATED: + mInput.mFilter.SetConvergence(value); + break; + case InputTypeBase::INTERPOLATED_MS: + case InputTypeBase::CUSTOM_VALUE_INTERP_MS: + mInput.mFilter.SetConvergenceInMs(value); + break; + case InputTypeBase::SAMPLED: + case InputTypeBase::CONSTANT_VALUE: + case InputTypeBase::CONSTANT_ARRAY: + ASSERT(false) << "Failed to set convergence on a non interpolated input type"; + break; + } + } + + void SetTarget(float value) { + switch (mType) { + case InputTypeBase::INTERPOLATED: + case InputTypeBase::INTERPOLATED_MS: + case InputTypeBase::CUSTOM_VALUE_INTERP_MS: + mInput.mFilter.SetTarget(value); + break; + case InputTypeBase::SAMPLED: + case InputTypeBase::CONSTANT_VALUE: + case InputTypeBase::CONSTANT_ARRAY: + break; + } + } + + void SetValue(float value) { + switch (mType) { + case InputTypeBase::INTERPOLATED: + case InputTypeBase::INTERPOLATED_MS: + case InputTypeBase::CUSTOM_VALUE_INTERP_MS: + mInput.mFilter.Value() = value; + break; + case InputTypeBase::SAMPLED: + case InputTypeBase::CONSTANT_VALUE: + case InputTypeBase::CONSTANT_ARRAY: + break; + } + } + + float GetValueNext() { + switch (mType) { + case InputTypeBase::INTERPOLATED: + case InputTypeBase::INTERPOLATED_MS: + case InputTypeBase::CUSTOM_VALUE_INTERP_MS: + return mInput.mFilter.Next(); + case InputTypeBase::SAMPLED: + case InputTypeBase::CONSTANT_ARRAY: + return *mInput.mIterator++; + case InputTypeBase::CONSTANT_VALUE: + return *mInput.mIterator; + } + return 0.0f; + } + + float GetValue() { + switch (mType) { + case InputTypeBase::INTERPOLATED: + case InputTypeBase::INTERPOLATED_MS: + case InputTypeBase::CUSTOM_VALUE_INTERP_MS: + return mInput.mFilter.Value(); + case InputTypeBase::SAMPLED: + case InputTypeBase::CONSTANT_VALUE: + case InputTypeBase::CONSTANT_ARRAY: + return *mInput.mIterator; + } + return 0.0f; + } + + float GetArrayValue(int32_t index) { + if (mType == InputTypeBase::CONSTANT_ARRAY) { + return mInput.mIterator[index]; + } + return 0.0f; + } + + float* GetArray() { + if (mType == InputTypeBase::CONSTANT_ARRAY) { + return mInput.mIterator.data(); + } + return nullptr; + } + + // run on each new batch call to setup input iterators for buffered data + void ProcessUpdate(std::vector& input, int32_t numSamples) { + switch (mType) { + case InputTypeBase::INTERPOLATED: + case InputTypeBase::INTERPOLATED_MS: + case InputTypeBase::CUSTOM_VALUE_INTERP_MS: + break; + case InputTypeBase::SAMPLED: + mInput.mIterator = input.at(mInputIndex).GetBufferIterator(numSamples); + break; + case InputTypeBase::CONSTANT_VALUE: + mInput.mIterator.Reset(&input.at(mInputIndex).Get(), 1); + break; + case InputTypeBase::CONSTANT_ARRAY: + mInput.mIterator = input.at(mInputIndex).GetArrayIterator(); + break; + } + } + + // run on each node update (can be node specific) and will update node rwa filters + void NodeUpdate(std::vector& input) { + switch (mType) { + case InputTypeBase::INTERPOLATED: + case InputTypeBase::INTERPOLATED_MS: + mInput.mFilter.SetTarget(input.at(mInputIndex).Get()); + break; + case InputTypeBase::CUSTOM_VALUE_INTERP_MS: // must set it manually + case InputTypeBase::SAMPLED: + case InputTypeBase::CONSTANT_VALUE: + case InputTypeBase::CONSTANT_ARRAY: + break; + } + } + + protected: + InputTypeBase mType; + int32_t mInputIndex = 0; + InputUnion mInput; + }; + } diff --git a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpControl.h b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpControl.h new file mode 100644 index 00000000..ccdf2841 --- /dev/null +++ b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpControl.h @@ -0,0 +1,134 @@ +#pragma once +#include "nodegraph/core/NodeGraphBase.h" + +#include "logging/LoggingAll.h" + +#include "hid/KeyboardPiano.h" +#include "hid/Midi.h" + +#include "audio/PortAudio.h" +#include "audio/AudioUtils.h" + +#include "math/MathFunc.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace l::nodegraph { + + class GraphControlBase : public NodeGraphOp { + public: + + static const int8_t mNumDefaultInputs = 7; + static const int8_t mNumDefaultOutputs = 2; + + GraphControlBase(NodeGraphBase* node, std::string_view name) : + NodeGraphOp(node, name), + mNodeInputManager(*this) + { + mNodeInputManager.AddInputBase(InputTypeBase::CONSTANT_VALUE, AddInput("Sync", 0.0f, 1, 0.0f, 1.0f)); + mNodeInputManager.AddInputBase(InputTypeBase::CONSTANT_VALUE, AddInput("Rate", 256.0f, 1, 1.0f, 2048.0f)); + mNodeInputManager.AddInputBase(InputTypeBase::INTERPOLATED, AddInput("Velocity", 0.5f, 1, 0.0f, 1.0f)); + mNodeInputManager.AddInputBase(InputTypeBase::INTERPOLATED, AddInput("Fade", 0.1f, 1, 0.0001f, 1.0f)); + mNodeInputManager.AddInputBase(InputTypeBase::INTERPOLATED, AddInput("Attack", 50.0f, 1, 1.0f, 10000.0f)); + mNodeInputManager.AddInputBase(InputTypeBase::INTERPOLATED, AddInput("Release", 50.0f, 1, 1.0f, 10000.0f)); + + AddOutput("Freq"); + AddOutput("Volume"); + + mSamplesUntilUpdate = 0.0f; + } + + virtual ~GraphControlBase() = default; + virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; + virtual void UpdateSignal(NodeInputManager& inputManager) = 0; + virtual std::pair ProcessSignal(NodeInputManager& inputManager) = 0; + protected: + float mUpdateRate = 256.0f; + + float mDeltaTime = 0.0f; + float mSamplesUntilUpdate = 0.0f; + + NodeInputManager mNodeInputManager; + }; + + /*********************************************************************/ + class GraphControlEnvelope : public GraphControlBase { + public: + GraphControlEnvelope(NodeGraphBase* node) : + GraphControlBase(node, "Envelope") + { + mFreqTargetId = mNodeInputManager.AddInputBase(InputTypeBase::CONSTANT_VALUE, AddInput("Freq")); + mFreqId = mNodeInputManager.AddInputBase(InputTypeBase::CUSTOM_VALUE_INTERP_MS); + mEnvelope = 0.0f; + } + + virtual ~GraphControlEnvelope() = default; + virtual void UpdateSignal(NodeInputManager& inputManager) override; + virtual std::pair ProcessSignal(NodeInputManager& inputManager) override; + protected: + int32_t mFrameCount = 0; + int32_t mFreqTargetId = 0; + int32_t mFreqId = 0; + int32_t mAttackFrames = 0; + int32_t mReleaseFrames = 0; + float mAttackFactor = 0.0f; + float mReleaseFactor = 0.0f; + float mEnvelopeTarget = 0.0f; + float mFreqTarget = 0.0f; + float mEnvelope = 0.0f; + }; + + /*********************************************************************/ + class GraphControlArpeggio: public NodeGraphOp { + public: + + const static int32_t gPolyphony = 12; + GraphControlArpeggio(NodeGraphBase* node) : + NodeGraphOp(node, "Arpeggio"), + mNodeInputManager(*this) + { + mNodeInputManager.AddInputBase(InputTypeBase::CONSTANT_VALUE, AddInput("Sync", 0.0f, 1, 0.0f, 1.0f)); + mNodeInputManager.AddInputBase(InputTypeBase::CONSTANT_VALUE, AddInput("Bpm", 60.0f, 1, 1.0f, 1000.0f)); + mNodeInputManager.AddInputBase(InputTypeBase::CONSTANT_VALUE, AddInput("Velocity", 0.5f, 1, 0.0f, 1.0f)); + mNodeInputManager.AddInputBase(InputTypeBase::CONSTANT_VALUE, AddInput("Fade", 0.1f, 1, 0.0001f, 1.0f)); + mNodeInputManager.AddInputBase(InputTypeBase::CONSTANT_VALUE, AddInput("Attack", 50.0f, 1, 1.0f, 10000.0f)); + mNodeInputManager.AddInputBase(InputTypeBase::CONSTANT_VALUE, AddInput("Release", 50.0f, 1, 1.0f, 10000.0f)); + mNodeInputManager.AddInputBase(InputTypeBase::CONSTANT_ARRAY, AddInput("Note On", l::audio::gNoNote_f, gPolyphony, -499.0, 500.0)); + mNodeInputManager.AddInputBase(InputTypeBase::CONSTANT_ARRAY, AddInput("Note Off", l::audio::gNoNote_f, gPolyphony, -499.0, 500.0)); + + AddOutput("Freq"); + AddOutput("Volume"); + + mGainTarget = 0.0f; + } + + virtual ~GraphControlArpeggio() = default; + virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; + void UpdateSignal(NodeInputManager& inputManager); + protected: + NodeInputManager mNodeInputManager; + + float mSamplesUntilUpdate = 0.0f; + float mUpdateRate = 256.0f; + + float mGainTarget = 0.0f; + float mGain = 0.0f; + float mGainAttack = 0.01f; + float mGainRelease = 0.01f; + + float mFreqSmoothing = 0.1f; + float mFreqTarget = 0.0f; + float mFreq = 0.0f; + std::vector mNotes; + int32_t mNoteIndex = 0; + + }; +} + diff --git a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpEffect.h b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpEffect.h index 3571eedd..a9f9d4a0 100644 --- a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpEffect.h +++ b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpEffect.h @@ -48,7 +48,7 @@ namespace l::nodegraph { virtual ~GraphEffectBase() = default; virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override final; virtual void UpdateSignal(std::vector&, std::vector&) {}; - virtual std::pair ProcessStereoSignal(float deltaTime, float value0, float value1) = 0; + virtual std::pair ProcessSignal(float value0, float value1) = 0; protected: float mUpdateRate = 256.0f; @@ -90,7 +90,7 @@ namespace l::nodegraph { virtual ~GraphEffectReverb2() = default; virtual void UpdateSignal(std::vector&, std::vector&) override; - virtual std::pair ProcessStereoSignal(float deltaTime, float value0, float value1) override; + virtual std::pair ProcessSignal(float value0, float value1) override; protected: std::vector mBuf0; std::vector mBuf1; @@ -222,79 +222,52 @@ namespace l::nodegraph { }; /*********************************************************************/ - class GraphEffectLimiter : public NodeGraphOp { + class GraphEffectLimiter : public GraphEffectBase { public: GraphEffectLimiter(NodeGraphBase* node) : - NodeGraphOp(node, "Limiter") + GraphEffectBase(node, "Limiter") { - AddInput("In 1"); - AddInput("In 2"); - AddInput("Attack", 5.0f, 1, 1.0f, 10000.0f); - AddInput("Release", 100.0f, 1, 1.0f, 10000.0f); AddInput("Preamp", 1.0f, 1, 0.0f, 10.0f); AddInput("Limit", 0.95f, 1, 0.0f, 10.0f); - - AddOutput("Out 1"); - AddOutput("Out 2"); - AddOutput("Envelope"); + AddInput("Attack", 5.0f, 1, 1.0f, 10000.0f); + AddInput("Release", 100.0f, 1, 1.0f, 10000.0f); mEnvelope = 0.0f; } virtual ~GraphEffectLimiter() = default; - virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; + virtual void UpdateSignal(std::vector&, std::vector&) override; + virtual std::pair ProcessSignal(float value0, float value1) override; protected: float mEnvelope = 0.0f; - }; - - /*********************************************************************/ - class GraphEffectEnvelope : public NodeGraphOp { - public: - GraphEffectEnvelope(NodeGraphBase* node) : - NodeGraphOp(node, "Envelope") - { - AddInput("Freq"); - AddInput("Velocity", 0.5f, 1, 0.0f, 1.0f); - AddInput("Attack", 50.0f, 1, 1.0f, 10000.0f); - AddInput("Release", 50.0f, 1, 1.0f, 10000.0f); - AddInput("Fade", 0.1f, 1, 0.0001f, 1.0f); - - AddOutput("Freq"); - AddOutput("Volume"); + float mAttack = 0.0; + float mRelease = 0.0; - mEnvelope = 0.0f; - } - - virtual ~GraphEffectEnvelope() = default; - virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; - virtual void Tick(int32_t, float) {} - protected: - int32_t mFrameCount = 0; - float mEnvelopeTarget = 0.0f; - float mFreq = 0.0f; - float mEnvelope = 0.0f; + l::audio::FilterRWA mFilterLimit; + l::audio::FilterRWA mFilterPreamp; }; + /*********************************************************************/ - class GraphEffectEnvelopeFollower : public NodeGraphOp { + class GraphEffectEnvelopeFollower : public GraphEffectBase { public: GraphEffectEnvelopeFollower(NodeGraphBase* node) : - NodeGraphOp(node, "Envelope Follower") + GraphEffectBase(node, "Envelope Follower") { - AddInput("In 1"); - AddInput("In 2"); AddInput("Attack", 5.0f, 1, 1.0f, 10000.0f); AddInput("Release", 100.0f, 1, 1.0f, 10000.0f); - AddOutput("Envelope"); mEnvelope = 0.0f; } virtual ~GraphEffectEnvelopeFollower() = default; - virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; + virtual void UpdateSignal(std::vector&, std::vector&) override; + virtual std::pair ProcessSignal(float value0, float value1) override; protected: float mEnvelope = 0.0f; + l::audio::FilterRWA mFilterAttack; + l::audio::FilterRWA mFilterRelease; }; /*********************************************************************/ @@ -366,43 +339,5 @@ namespace l::nodegraph { float mGateSmoothingNeg = 0.01f; }; - /*********************************************************************/ - class GraphEffectArpeggio: public NodeGraphOp { - public: - - const static int32_t gPolyphony = 12; - GraphEffectArpeggio(NodeGraphBase* node) : - NodeGraphOp(node, "Arpeggio") - { - AddInput("Note On", l::audio::gNoNote_f, gPolyphony, -499.0, 500.0); - AddInput("Note Off", l::audio::gNoNote_f, gPolyphony, -499.0, 500.0); - AddInput("Velocity", 1.0f, 1, 0.0f, 1.0f); - AddInput("Bpm", 60.0f, 1, 1.0f, 1000.0f); - AddInput("Fmod", 1.0f, 1, 0.01f, 1.0f); - AddInput("Attack", 0.01f, 1, 0.0f, 1.0f); - AddInput("Smooth", 0.0f, 1, 0.0f, 1.0f); - AddInput("Sync", 0.0f, 1, 0.0f, 1.0f); - - AddOutput("Freq"); - AddOutput("Volume"); - - mGainTarget = 0.0f; - } - - virtual ~GraphEffectArpeggio() = default; - virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; - protected: - float mSamplesUntilUpdate = 0.0f; - float mGainTarget = 0.0f; - float mGain = 0.0f; - float mGainSmoothing = 0.01f; - float mGainSmoothingNeg = 0.01f; - - float mFreqSmoothing = 0.1f; - float mFreqTarget = 0.0f; - float mFreq = 0.0f; - std::vector mNotes; - int32_t mNoteIndex = 0; - }; } diff --git a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpInput.h b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpInput.h index b4e2c079..c6406c44 100644 --- a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpInput.h +++ b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpInput.h @@ -78,7 +78,7 @@ namespace l::nodegraph { AddConstant("Velocity", 0.0f); AddConstant("Note On", l::audio::gNoNote_f, gPolyphony); AddConstant("Note Off", l::audio::gNoNote_f, gPolyphony); - AddConstant("Device Id", 0.0f, 0.0f, 10.0f); + AddConstant("Device Id", 0.0f, 1, 0.0f, 10.0f); mChannel.resize(1); @@ -149,7 +149,7 @@ namespace l::nodegraph { AddConstant("Knob " + std::to_string(i), 0.0f); } AddOutput("Device Id", 0.0f); - AddConstant("Device Id", 0.0f, 0.0f, 10.0f); + AddConstant("Device Id", 0.0f, 1, 0.0f, 10.0f); SetDevice(0); @@ -236,7 +236,7 @@ namespace l::nodegraph { AddConstant("Pad " + std::to_string(i), 0.0f); } AddOutput("Device Id", 0.0f); - AddConstant("Device Id", 0.0f, 0.0f, 10.0f); + AddConstant("Device Id", 0.0f, 1, 0.0f, 10.0f); mButtonStates.resize(8); diff --git a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpNumeric.h b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpNumeric.h index 41cd599a..012d3555 100644 --- a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpNumeric.h +++ b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpNumeric.h @@ -34,8 +34,8 @@ namespace l::nodegraph { } virtual ~GraphNumericAdd() = default; virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override { - auto input0 = inputs.at(0).GetIterator(numSamples); - auto input1 = inputs.at(1).GetIterator(numSamples); + auto input0 = inputs.at(0).GetBufferIterator(numSamples); + auto input1 = inputs.at(1).GetBufferIterator(numSamples); auto output = outputs.at(0).GetIterator(numSamples); if (numSamples > 1) { @@ -61,8 +61,8 @@ namespace l::nodegraph { virtual ~GraphNumericMultiply() = default; virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override { - auto input0 = inputs.at(0).GetIterator(numSamples); - auto input1 = inputs.at(1).GetIterator(numSamples); + auto input0 = inputs.at(0).GetBufferIterator(numSamples); + auto input1 = inputs.at(1).GetBufferIterator(numSamples); auto output = outputs.at(0).GetIterator(numSamples); if (numSamples > 1) { @@ -87,8 +87,8 @@ namespace l::nodegraph { } virtual ~GraphNumericSubtract() = default; virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override { - auto input0 = inputs.at(0).GetIterator(numSamples); - auto input1 = inputs.at(1).GetIterator(numSamples); + auto input0 = inputs.at(0).GetBufferIterator(numSamples); + auto input1 = inputs.at(1).GetBufferIterator(numSamples); auto output = outputs.at(0).GetIterator(numSamples); if (numSamples > 1) { @@ -113,7 +113,7 @@ namespace l::nodegraph { virtual ~GraphNumericNegate() = default; virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override { - auto input0 = inputs.at(0).GetIterator(numSamples); + auto input0 = inputs.at(0).GetBufferIterator(numSamples); auto output = outputs.at(0).GetIterator(numSamples); if (numSamples > 1) { @@ -138,7 +138,7 @@ namespace l::nodegraph { virtual ~GraphNumericIntegral() = default; virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override { - auto input0 = inputs.at(0).GetIterator(numSamples); + auto input0 = inputs.at(0).GetBufferIterator(numSamples); auto output = outputs.at(0).GetIterator(numSamples); if (numSamples > 1) { @@ -168,9 +168,9 @@ namespace l::nodegraph { virtual ~GraphNumericMultiply3() = default; virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override { - auto input0 = inputs.at(0).GetIterator(numSamples); - auto input1 = inputs.at(1).GetIterator(numSamples); - auto input2 = inputs.at(2).GetIterator(numSamples); + auto input0 = inputs.at(0).GetBufferIterator(numSamples); + auto input1 = inputs.at(1).GetBufferIterator(numSamples); + auto input2 = inputs.at(2).GetBufferIterator(numSamples); auto output = outputs.at(0).GetIterator(numSamples); if (numSamples > 1) { @@ -197,9 +197,9 @@ namespace l::nodegraph { virtual ~GraphNumericMultiplyAndAdd() = default; void virtual Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override { - auto input0 = inputs.at(0).GetIterator(numSamples); - auto input1 = inputs.at(1).GetIterator(numSamples); - auto input2 = inputs.at(2).GetIterator(numSamples); + auto input0 = inputs.at(0).GetBufferIterator(numSamples); + auto input1 = inputs.at(1).GetBufferIterator(numSamples); + auto input2 = inputs.at(2).GetBufferIterator(numSamples); auto output = outputs.at(0).GetIterator(numSamples); if (numSamples > 1) { @@ -226,7 +226,7 @@ namespace l::nodegraph { virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override { outputs.at(0).mOutput = l::math::functions::round(inputs.at(0).Get()); - auto input0 = inputs.at(2).GetIterator(numSamples); + auto input0 = inputs.at(2).GetBufferIterator(numSamples); auto output = outputs.at(0).GetIterator(numSamples); if (numSamples > 1) { diff --git a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpOutput.h b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpOutput.h index a74f45f4..61e0b79c 100644 --- a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpOutput.h +++ b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpOutput.h @@ -29,7 +29,7 @@ namespace l::nodegraph { NodeGraphOp(node, "Debug") { AddInput("Debug"); - AddConstant("Smooth", 0.5f, 0.0f, 1.0f); + AddConstant("Smooth", 0.5f, 1, 0.0f, 1.0f); AddConstant(""); mValue = 0.0; diff --git a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpSource.h b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpSource.h index 49406114..9efa0ab5 100644 --- a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpSource.h +++ b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpSource.h @@ -41,10 +41,10 @@ namespace l::nodegraph { AddOutput("Out 3"); auto bound = GetInputBounds(mModeToBoundList[mMode]); - AddConstant("Out 1", 0.0f, bound.first, bound.second); - AddConstant("Out 2", 0.0f, bound.first, bound.second); - AddConstant("Out 3", 0.0f, bound.first, bound.second); - AddConstant("Out 4", 0.0f, bound.first, bound.second); + AddConstant("Out 1", 0.0f, 1, bound.first, bound.second); + AddConstant("Out 2", 0.0f, 1, bound.first, bound.second); + AddConstant("Out 3", 0.0f, 1, bound.first, bound.second); + AddConstant("Out 4", 0.0f, 1, bound.first, bound.second); } virtual ~GraphSourceConstants() = default; diff --git a/packages/nodegraph/source/common/NodeGraphSchema.cpp b/packages/nodegraph/source/common/NodeGraphSchema.cpp index bfcbd7ff..8b24f7c7 100644 --- a/packages/nodegraph/source/common/NodeGraphSchema.cpp +++ b/packages/nodegraph/source/common/NodeGraphSchema.cpp @@ -40,6 +40,7 @@ namespace l::nodegraph { case 4: node = mMainNodeGraph.NewNode(OutputType::ExternalOutput, mAudioOutput != nullptr ? mAudioOutput->GetSampleRate() : 44100, 60); break; + case 50: node = mMainNodeGraph.NewNode(OutputType::Default); break; @@ -64,6 +65,7 @@ namespace l::nodegraph { case 57: node = mMainNodeGraph.NewNode(OutputType::Default); break; + case 100: node = mMainNodeGraph.NewNode(OutputType::Default); break; @@ -73,6 +75,7 @@ namespace l::nodegraph { case 102: node = mMainNodeGraph.NewNode(OutputType::Default); break; + case 150: node = mMainNodeGraph.NewNode(OutputType::Default); break; @@ -82,6 +85,7 @@ namespace l::nodegraph { case 152: node = mMainNodeGraph.NewNode(OutputType::Default); break; + case 200: node = mMainNodeGraph.NewNode(OutputType::ExternalOutput); break; @@ -91,9 +95,7 @@ namespace l::nodegraph { case 202: node = mMainNodeGraph.NewNode(OutputType::ExternalVisualOutput, 100); break; - case 250: - node = mMainNodeGraph.NewNode(OutputType::Default); - break; + case 251: node = mMainNodeGraph.NewNode(OutputType::Default); break; @@ -112,9 +114,7 @@ namespace l::nodegraph { case 257: node = mMainNodeGraph.NewNode(OutputType::Default); break; - case 258: - node = mMainNodeGraph.NewNode(OutputType::Default); - break; + case 300: node = mMainNodeGraph.NewNode(OutputType::Default, mKeyState); break; @@ -139,6 +139,7 @@ namespace l::nodegraph { case 307: node = mMainNodeGraph.NewNode(OutputType::Default, mMidiManager, 4); break; + case 350: node = mMainNodeGraph.NewNode(OutputType::Default); break; @@ -160,6 +161,14 @@ namespace l::nodegraph { case 356: node = mMainNodeGraph.NewNode(OutputType::Default); break; + + case 400: + node = mMainNodeGraph.NewNode(OutputType::Default); + break; + case 401: + node = mMainNodeGraph.NewNode(OutputType::Default); + break; + default: ASSERT(typeId < 10000) << "Custom node id's begin at id 1000"; if (mCreateCustomNode) { diff --git a/packages/nodegraph/source/common/core/NodeGraphBase.cpp b/packages/nodegraph/source/common/core/NodeGraphBase.cpp index e2875f0c..2f0c067a 100644 --- a/packages/nodegraph/source/common/core/NodeGraphBase.cpp +++ b/packages/nodegraph/source/common/core/NodeGraphBase.cpp @@ -105,9 +105,16 @@ namespace l::nodegraph { if (!input.HasInputNode()) { return false; } - input.mInputType = InputType::INPUT_EMPTY; + input.mInput.mInputNode = nullptr; input.mInputFromOutputChannel = 0; + + if (input.mInputBuf) { + input.mInputType = InputType::INPUT_ARRAY; + } + else { + input.mInputType = InputType::INPUT_CONSTANT; + } return true; } @@ -334,22 +341,29 @@ namespace l::nodegraph { return mName; } - void NodeGraphOp::AddInput(std::string_view name, float defaultValue, int32_t size, float boundMin, float boundMax, bool visible, bool editable) { + float NodeGraphOp::GetDefaultData(int8_t inputChannel) { + return std::get<0>(mDefaultInData.at(inputChannel)); + } + + int32_t NodeGraphOp::AddInput(std::string_view name, float defaultValue, int32_t size, float boundMin, float boundMax, bool visible, bool editable) { mNumInputs++; mDefaultInStrings.push_back(std::string(name)); mDefaultInData.push_back({ defaultValue, size, boundMin, boundMax, false, visible, editable}); + return static_cast(mDefaultInData.size() - 1); } - void NodeGraphOp::AddOutput(std::string_view name, float defaultValue, int32_t size) { + int32_t NodeGraphOp::AddOutput(std::string_view name, float defaultValue, int32_t size) { mNumOutputs++; mDefaultOutStrings.push_back(std::string(name)); mDefaultOutData.push_back({ defaultValue, size }); + return static_cast(mDefaultOutData.size() - 1); } - void NodeGraphOp::AddConstant(std::string_view name, float defaultValue, float boundMin, float boundMax, bool visible, bool editable) { + int32_t NodeGraphOp::AddConstant(std::string_view name, float defaultValue, int32_t size, float boundMin, float boundMax, bool visible, bool editable) { mNumInputs++; mDefaultInStrings.push_back(std::string(name)); - mDefaultInData.push_back({ defaultValue, 1, boundMin, boundMax, true, visible, editable}); + mDefaultInData.push_back({ defaultValue, size, boundMin, boundMax, true, visible, editable}); + return static_cast(mDefaultInData.size() - 1); } diff --git a/packages/nodegraph/source/common/core/NodeGraphInput.cpp b/packages/nodegraph/source/common/core/NodeGraphInput.cpp index 285bfcfb..e8ee7c65 100644 --- a/packages/nodegraph/source/common/core/NodeGraphInput.cpp +++ b/packages/nodegraph/source/common/core/NodeGraphInput.cpp @@ -53,7 +53,7 @@ namespace l::nodegraph { } if (static_cast(mInputBuf->size()) < size) { mInputBuf->resize(size); - for (int32_t i = 0; i < mInputBuf->size();i++) { + for (size_t i = 0; i < mInputBuf->size();i++) { (*mInputBuf)[i] = 0.0f; } } @@ -68,7 +68,7 @@ namespace l::nodegraph { return mInput.mInputFloatConstant; } - NodeInputDataIterator NodeGraphInput::GetIterator(int32_t numSamples) { + NodeInputDataIterator NodeGraphInput::GetBufferIterator(int32_t numSamples) { auto size = GetSize(); if (size > 1) { ASSERT(size == numSamples); @@ -76,6 +76,11 @@ namespace l::nodegraph { return NodeInputDataIterator(&Get(numSamples), size); } + NodeInputDataIterator NodeGraphInput::GetArrayIterator() { + auto size = GetSize(); + return NodeInputDataIterator(&Get(size), size); + } + int32_t NodeGraphInput::GetSize() { switch (mInputType) { case InputType::INPUT_NODE: diff --git a/packages/nodegraph/source/common/core/NodeGraphOutput.cpp b/packages/nodegraph/source/common/core/NodeGraphOutput.cpp index e11ba8c2..04c2e77f 100644 --- a/packages/nodegraph/source/common/core/NodeGraphOutput.cpp +++ b/packages/nodegraph/source/common/core/NodeGraphOutput.cpp @@ -17,7 +17,7 @@ namespace l::nodegraph { } if (static_cast(mOutputBuf->size()) < size) { mOutputBuf->resize(size); - for (int32_t i = 0; i < mOutputBuf->size(); i++) { + for (size_t i = 0; i < mOutputBuf->size(); i++) { (*mOutputBuf)[i] = 0.0f; } } diff --git a/packages/nodegraph/source/common/operations/NodeGraphOpControl.cpp b/packages/nodegraph/source/common/operations/NodeGraphOpControl.cpp new file mode 100644 index 00000000..216897b4 --- /dev/null +++ b/packages/nodegraph/source/common/operations/NodeGraphOpControl.cpp @@ -0,0 +1,214 @@ +#include "nodegraph/operations/NodeGraphOpControl.h" + +#include "logging/Log.h" +#include "audio/AudioUtils.h" + +#include "math/MathFunc.h" +#include "math/MathSmooth.h" + +#include + +namespace l::nodegraph { + /*********************************************************************/ + + void GraphControlBase::Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) { + float sync = inputs.at(0).Get(); + if (sync > 0.5f) { + mSamplesUntilUpdate = 0; + mUpdateRate = mNodeInputManager.GetValue(1); + } + + auto output0 = outputs.at(0).GetIterator(numSamples); + auto output1 = outputs.at(1).GetIterator(numSamples); + + mNodeInputManager.ProcessUpdate(inputs, numSamples); + + mSamplesUntilUpdate = l::audio::BatchUpdate(mUpdateRate, mSamplesUntilUpdate, 0, numSamples, + [&]() { + mNodeInputManager.NodeUpdate(inputs); + mUpdateRate = mNodeInputManager.GetValue(1); + + UpdateSignal(mNodeInputManager); + }, + [&](int32_t start, int32_t end, bool) { + for (int32_t i = start; i < end; i++) { + auto [out0, out1] = ProcessSignal(mNodeInputManager); + *output0++ = out0; + *output1++ = out1; + } + } + ); + } + + /*********************************************************************/ + + void GraphControlEnvelope::UpdateSignal(NodeInputManager& inputManager) { + + mFreqTarget = inputManager.GetValueNext(mFreqTargetId); + inputManager.SetTarget(mFreqId, mFreqTarget); + mAttackFrames = static_cast(inputManager.GetValue(4)); + mReleaseFrames = static_cast(inputManager.GetValue(5)); + mAttackFactor = l::audio::GetRWAFactorFromTicks(inputManager.GetValueNext(4), 0.001f); + mReleaseFactor = l::audio::GetRWAFactorFromTicks(inputManager.GetValueNext(5), 0.001f); + } + + std::pair GraphControlEnvelope::ProcessSignal(NodeInputManager& inputManager) { + float velocity = inputManager.GetValueNext(2); + float freq = inputManager.GetValue(mFreqId); + + if (mFreqTarget == 0.0f) { + // trigger release + if (mFrameCount > 0 && mFrameCount < mAttackFrames + 1) { + mFrameCount = mAttackFrames + 2; + } + else if (mFrameCount > mAttackFrames + 1 + mReleaseFrames) { + mFrameCount = 0; + } + } + else { + if (mFrameCount == 0) { + freq = mFreqTarget; + } + else if (mFreqTarget != 0) { + mFrameCount = mAttackFrames + 2; + mEnvelopeTarget = velocity; + } + } + + if (mFreqTarget != 0 && mFrameCount < mAttackFrames) { + // attack + mEnvelopeTarget = velocity; + mFrameCount++; + } + else if (mFreqTarget != 0 && mFrameCount == mAttackFrames + 1) { + // sustain + } + else if (mFreqTarget == 0 && mFrameCount > mAttackFrames + 1) { + // release + mEnvelopeTarget = 0.0f; + mFrameCount++; + if (mFrameCount > mAttackFrames + 1 + mReleaseFrames) { + mFrameCount = 0; + } + } + + float delta = mEnvelopeTarget - mEnvelope; + if (delta > 0) { + mEnvelope += mAttackFactor * delta; + } + else { + mEnvelope += mReleaseFactor * delta; + } + + if (mFreqTarget != 0.0f) { + // note on + freq = inputManager.GetValueNext(mFreqId); + } + else { + // note off + } + + return { freq, l::math::functions::pow(mEnvelope, 0.5f) }; + } + + /*********************************************************************/ + + void GraphControlArpeggio::UpdateSignal(NodeInputManager& inputManager) { + float attack = inputManager.GetValue(4); + float release = inputManager.GetValue(5); + float attackFactor = l::audio::GetRWAFactorFromMS(attack, 0.001f); + float releaseFactor = l::audio::GetRWAFactorFromMS(release, 0.001f); + mGainAttack = attackFactor; + mGainRelease = releaseFactor; + + if (mNotes.empty()) { + mGainTarget = 0.0f; + } + else { + mNoteIndex = mNoteIndex % mNotes.size(); + mFreqTarget = l::audio::GetFrequencyFromNote(static_cast(mNotes.at(mNoteIndex))); + mNoteIndex++; + + auto velocity = inputManager.GetValue(2); + auto fade = inputManager.GetValue(3); + + mGainTarget = velocity; + mFreqSmoothing = fade; + mFreqSmoothing *= mFreqSmoothing * 0.5f; + } + } + + void GraphControlArpeggio::Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) { + mNodeInputManager.ProcessUpdate(inputs, numSamples); + + float sync = mNodeInputManager.GetValue(0); + float bpm = mNodeInputManager.GetValueNext(1); + + mUpdateRate = 44100.0f * 60.0f / (4.0f * bpm); + + if (sync > 0.5f) { + mSamplesUntilUpdate = 0; + } + + { + auto noteIdsOn = mNodeInputManager.GetArray(6); + auto noteIdsOff = mNodeInputManager.GetArray(7); + + if (!mNotes.empty()) { + for (int32_t i = 0; i < gPolyphony; i++) { + if (l::math::functions::equal(*noteIdsOff, l::audio::gNoNote_f)) { + break; + } + int32_t noteOffId = static_cast(*noteIdsOff + 0.5f); + auto it = std::find(mNotes.begin(), mNotes.end(), noteOffId); + if (it != mNotes.end()) { + mNotes.erase(it); + } + noteIdsOff++; + } + } + + if (mNotes.empty()) { + mGainTarget = 0.0f; + } + for (int32_t i = 0; i < gPolyphony; i++) { + if (l::math::functions::equal(*noteIdsOn, l::audio::gNoNote_f)) { + break; + } + int32_t noteOnId = static_cast(*noteIdsOn + 0.5f); + auto it = std::find(mNotes.begin(), mNotes.end(), noteOnId); + if (it == mNotes.end()) { + mNotes.push_back(noteOnId); + } + noteIdsOn++; + } + } + + auto output0 = outputs.at(0).GetIterator(numSamples); + auto output1 = outputs.at(1).GetIterator(numSamples); + mSamplesUntilUpdate = l::audio::BatchUpdate(mUpdateRate, mSamplesUntilUpdate, 0, numSamples, + [&]() { + mNodeInputManager.NodeUpdate(inputs); + mUpdateRate = mNodeInputManager.GetValue(1); + + UpdateSignal(mNodeInputManager); + }, + [&](int32_t start, int32_t end, bool) { + for (int32_t i = start; i < end; i++) { + float delta = mGainTarget - mGain; + if (delta > 0) { + mGain += mGainAttack * l::math::smooth::smoothPolyh3(delta); + } + else { + mGain += mGainRelease * (-l::math::smooth::smoothPolyh3(-delta)); + } + mFreq += mFreqSmoothing * (mFreqTarget - mFreq); + + *output0++ = mFreq; + *output1++ = mGain; + } + } + ); + } + +} diff --git a/packages/nodegraph/source/common/operations/NodeGraphOpEffect.cpp b/packages/nodegraph/source/common/operations/NodeGraphOpEffect.cpp index 703357c8..087cfcac 100644 --- a/packages/nodegraph/source/common/operations/NodeGraphOpEffect.cpp +++ b/packages/nodegraph/source/common/operations/NodeGraphOpEffect.cpp @@ -17,8 +17,8 @@ namespace l::nodegraph { mSamplesUntilUpdate = 0; } - auto input0 = inputs.at(4).GetIterator(numSamples); - auto input1 = inputs.at(5).GetIterator(numSamples); + auto input0 = inputs.at(4).GetBufferIterator(numSamples); + auto input1 = inputs.at(5).GetBufferIterator(numSamples); auto output0 = outputs.at(0).GetIterator(numSamples); auto output1 = outputs.at(1).GetIterator(numSamples); @@ -31,8 +31,6 @@ namespace l::nodegraph { mFilterGain.SetTarget(inputs.at(2).Get()); mFilterMix.SetTarget(inputs.at(3).Get()); - mDeltaTime = 1.0f / 44100.0f; - UpdateSignal(inputs, outputs); }, [&](int32_t start, int32_t end, bool) { @@ -43,7 +41,7 @@ namespace l::nodegraph { auto in0 = *input0++; auto in1 = *input1++; - auto [out0, out1] = ProcessStereoSignal(mDeltaTime, in0, in1); + auto [out0, out1] = ProcessSignal(in0, in1); *output0++ = mix * out0 + antimix * in0; *output1++ = mix * out1 + antimix * in1; } @@ -70,7 +68,7 @@ namespace l::nodegraph { mDelay2 = (int(mBufIndex + d2 * mBufSizeLimit)) % mBufSizeLimit; } - std::pair GraphEffectReverb2::ProcessStereoSignal(float, float value0, float value1) { + std::pair GraphEffectReverb2::ProcessSignal(float value0, float value1) { float out0 = (fb1 * mBuf1[mDelay1] + fb0 * mBuf0[mDelay0] + fb2 * mBuf0[mDelay2]); float out1 = (fb1 * mBuf0[mDelay1] + fb0 * mBuf1[mDelay0] + fb2 * mBuf1[mDelay2]); @@ -242,124 +240,59 @@ namespace l::nodegraph { /*********************************************************************/ - void GraphEffectLimiter::Process(int32_t, 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(); + void GraphEffectLimiter::UpdateSignal(std::vector& inputs, std::vector&) { + mFilterPreamp.SetTarget(inputs.at(mNumDefaultInputs + 0).Get()); + mFilterLimit.SetTarget(inputs.at(mNumDefaultInputs + 1).Get()); + float attackMs = inputs.at(mNumDefaultInputs + 2).Get(); + float releaseMs = inputs.at(mNumDefaultInputs + 3).Get(); + mAttack = l::audio::GetRWAFactorFromMS(attackMs, 0.001f); + mRelease = l::audio::GetRWAFactorFromMS(releaseMs, 0.001f); + } - float in0 = inputs.at(0).Get(); - float in1 = inputs.at(1).Get(); + std::pair GraphEffectLimiter::ProcessSignal(float value0, float value1) { + float limit = mFilterLimit.Next(); + float preamp = mFilterPreamp.Next(); - float inVal0 = preamp * in0; - float inVal1 = preamp * in1; + float inVal0 = preamp * value0; + float inVal1 = preamp * value1; float inVal = inVal0 > inVal1 ? inVal0 : inVal1; if (inVal > mEnvelope) { - mEnvelope = attack * (mEnvelope - inVal) + inVal; + mEnvelope = mAttack * (mEnvelope - inVal) + inVal; } else { - mEnvelope = release * (mEnvelope - inVal) + inVal; + mEnvelope = mRelease * (mEnvelope - inVal) + inVal; } float envelopeAbs = l::math::functions::abs(mEnvelope); + float out0; + float out1; if (envelopeAbs > limit) { if (envelopeAbs > 1.0f) { - outputs.at(0).mOutput = inVal0 / mEnvelope; - outputs.at(1).mOutput = inVal1 / mEnvelope; + out0 = inVal0 / mEnvelope; + out1 = inVal1 / mEnvelope; } else { - outputs.at(0).mOutput = inVal0 / (1.0f + mEnvelope - limit); - outputs.at(1).mOutput = inVal1 / (1.0f + mEnvelope - limit); + out0 = inVal0 / (1.0f + mEnvelope - limit); + out1 = inVal1 / (1.0f + mEnvelope - limit); } } else { - outputs.at(0).mOutput = in0; - outputs.at(1).mOutput = in1; + out0 = value0; + out1 = value1; } - outputs.at(2).mOutput = envelopeAbs; + return { out0, out1 }; } /*********************************************************************/ - - void GraphEffectEnvelope::Process(int32_t, std::vector& inputs, std::vector& outputs) { - float freqTarget = inputs.at(0).Get(); - float velocity = l::math::functions::pow(inputs.at(1).Get(), 0.5f); - int32_t attackFrames = static_cast(inputs.at(2).Get() * 44100.0f / 1000.0f); - int32_t releaseFrames = static_cast(inputs.at(3).Get() * 44100.0f / 1000.0f); - float freqFade = inputs.at(4).Get(); - - float attackFade = 1.0f - l::math::functions::pow(0.001f, 1.0f / (inputs.at(2).Get() * 44100.0f * 0.001f)); - float releaseFade = 1.0f - l::math::functions::pow(0.001f, 1.0f / (inputs.at(3).Get() * 44100.0f * 0.001f)); - - if (freqTarget == 0.0f) { - // trigger release - if (mFrameCount > 0 && mFrameCount < attackFrames + 1) { - mFrameCount = attackFrames + 2; - } - else if (mFrameCount > attackFrames + 1 + releaseFrames) { - mFrameCount = 0; - } - } - else { - if (mFrameCount == 0) { - mFreq = freqTarget; - } - else if (freqTarget != 0) { - mFrameCount = attackFrames + 2; - mEnvelopeTarget = velocity; - } - } - - if (freqTarget != 0 && mFrameCount < attackFrames) { - // attack - mEnvelopeTarget = velocity; - mFrameCount++; - } - else if (freqTarget != 0 && mFrameCount == attackFrames + 1) { - // sustain - } - else if (freqTarget == 0 && mFrameCount > attackFrames + 1) { - // release - mEnvelopeTarget = 0.0f; - mFrameCount++; - if (mFrameCount > attackFrames + 1 + releaseFrames) { - mFrameCount = 0; - } - } - - float delta = mEnvelopeTarget - mEnvelope; - if (delta > 0) { - mEnvelope += attackFade * delta; - } - else { - mEnvelope += releaseFade * delta; - } - - if (freqTarget != 0.0f) { - // note on - mFreq += freqFade * freqFade * (freqTarget - mFreq); - } - else { - // note off - } - - outputs.at(0).mOutput = mFreq; - outputs.at(1).mOutput = l::math::functions::pow(mEnvelope, 0.5f); + void GraphEffectEnvelopeFollower::UpdateSignal(std::vector& inputs, std::vector&) { + mFilterAttack.SetConvergenceInMs(inputs.at(2).Get()); + mFilterAttack.SetConvergenceInMs(inputs.at(3).Get()); } - /*********************************************************************/ - void GraphEffectEnvelopeFollower::Process(int32_t, 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 = in0 > in1 ? in0 : in1; + std::pair GraphEffectEnvelopeFollower::ProcessSignal(float value0, float value1) { + float inVal = value0 > value1 ? value0 : value1; + float attack = mFilterAttack.Next(); + float release = mFilterRelease.Next(); if (inVal > mEnvelope) { mEnvelope = attack * (mEnvelope - inVal) + inVal; } @@ -368,10 +301,11 @@ namespace l::nodegraph { } float envelopeAbs = l::math::functions::abs(mEnvelope); - outputs.at(0).mOutput = envelopeAbs; + return { envelopeAbs * value0, envelopeAbs * value1 }; } /*********************************************************************/ + void GraphEffectSaturator::Process(int32_t, std::vector& inputs, std::vector& outputs) { float wet = inputs.at(2).Get(); float preamp = inputs.at(3).Get(); @@ -455,84 +389,4 @@ namespace l::nodegraph { } }); } - - /*********************************************************************/ - void GraphEffectArpeggio::Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) { - auto noteIdsOn = &inputs.at(0).Get(gPolyphony); - auto noteIdsOff = &inputs.at(1).Get(gPolyphony); - float velocity = inputs.at(2).Get(); - float bpm = inputs.at(3).Get(); - float fmod = inputs.at(4).Get(); - float attack = inputs.at(5).Get(); - - if (inputs.at(7).Get() > 0.5f) { - mSamplesUntilUpdate = 0.0f; - } - - if (!mNotes.empty()) { - for (int32_t i = 0; i < gPolyphony; i++) { - if (l::math::functions::equal(*noteIdsOff, l::audio::gNoNote_f)) { - break; - } - int32_t noteOffId = static_cast(*noteIdsOff + 0.5f); - auto it = std::find(mNotes.begin(), mNotes.end(), noteOffId); - if (it != mNotes.end()) { - mNotes.erase(it); - } - noteIdsOff++; - } - } - - if (mNotes.empty()) { - mSamplesUntilUpdate = 0.0f; - mGainTarget = 0.0f; - } - for (int32_t i = 0; i < gPolyphony; i++) { - if (l::math::functions::equal(*noteIdsOn, l::audio::gNoNote_f)) { - break; - } - int32_t noteOnId = static_cast(*noteIdsOn + 0.5f); - auto it = std::find(mNotes.begin(), mNotes.end(), noteOnId); - if (it == mNotes.end()) { - mNotes.push_back(noteOnId); - } - noteIdsOn++; - } - - mGainSmoothing = attack * attack; - mGainSmoothingNeg = mGainSmoothing; - - float freq = 44100.0f * 60.0f / bpm; - - mSamplesUntilUpdate = l::audio::BatchUpdate(freq * fmod, mSamplesUntilUpdate, 0, numSamples, - [&]() { - if (mNotes.empty()) { - mGainTarget = 0.0f; - } - else { - mNoteIndex = mNoteIndex % mNotes.size(); - mFreqTarget = l::audio::GetFrequencyFromNote(static_cast(mNotes.at(mNoteIndex))); - mNoteIndex++; - mGainTarget = velocity; - mFreqSmoothing = inputs.at(6).Get(); - mFreqSmoothing *= mFreqSmoothing * 0.5f; - } - }, - [&](int32_t start, int32_t end, bool) { - for (int32_t i = start; i < end; i++) { - float delta = mGainTarget - mGain; - if (delta > 0) { - mGain += mGainSmoothing * l::math::smooth::smoothPolyh3(delta); - } - else { - mGain += mGainSmoothingNeg * (-l::math::smooth::smoothPolyh3(-delta)); - } - - mFreq += mFreqSmoothing * (mFreqTarget - mFreq); - outputs.at(0).mOutput = mFreq; - outputs.at(1).mOutput = mGain; - } - }); - } - } diff --git a/packages/nodegraph/source/common/operations/NodeGraphOpOutput.cpp b/packages/nodegraph/source/common/operations/NodeGraphOpOutput.cpp index af7ef5b4..b2a2a4b3 100644 --- a/packages/nodegraph/source/common/operations/NodeGraphOpOutput.cpp +++ b/packages/nodegraph/source/common/operations/NodeGraphOpOutput.cpp @@ -19,10 +19,13 @@ namespace l::nodegraph { /*********************************************************************/ void GraphOutputSpeaker::Process(int32_t numSamples, std::vector& inputs, std::vector&) { + if (mAudioStream == nullptr) { + return; + } auto& buffer = mAudioStream->GetWriteBuffer(); - auto in0 = inputs.at(0).GetIterator(numSamples); - auto in1 = inputs.at(1).GetIterator(numSamples); + auto in0 = inputs.at(0).GetBufferIterator(numSamples); + auto in1 = inputs.at(1).GetBufferIterator(numSamples); mSamplesUntilUpdate = l::audio::BatchUpdate(256.0f, mSamplesUntilUpdate, 0, numSamples, [&]() { diff --git a/packages/nodegraph/source/common/operations/NodeGraphOpSource.cpp b/packages/nodegraph/source/common/operations/NodeGraphOpSource.cpp index f97329e9..28a82e50 100644 --- a/packages/nodegraph/source/common/operations/NodeGraphOpSource.cpp +++ b/packages/nodegraph/source/common/operations/NodeGraphOpSource.cpp @@ -13,7 +13,7 @@ namespace l::nodegraph { /*********************************************************************/ void GraphSourceConstants::Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) { - auto input0 = inputs.at(0).GetIterator(numSamples); + auto input0 = inputs.at(0).GetBufferIterator(numSamples); auto output0 = outputs.at(0).GetIterator(numSamples); for (int8_t i = 0; i < mNumOutputs; i++) { @@ -28,10 +28,10 @@ namespace l::nodegraph { /*********************************************************************/ void GraphSourceTime::Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) { - auto input0 = inputs.at(0).GetIterator(numSamples); - auto input1 = inputs.at(1).GetIterator(numSamples); - auto input2 = inputs.at(2).GetIterator(numSamples); - auto input3 = inputs.at(3).GetIterator(numSamples); + auto input0 = inputs.at(0).GetBufferIterator(numSamples); + auto input1 = inputs.at(1).GetBufferIterator(numSamples); + auto input2 = inputs.at(2).GetBufferIterator(numSamples); + auto input3 = inputs.at(3).GetBufferIterator(numSamples); auto output0 = outputs.at(0).GetIterator(numSamples); auto output1 = outputs.at(1).GetIterator(numSamples); auto output2 = outputs.at(2).GetIterator(numSamples); diff --git a/packages/nodegraph/tests/common/NodeGraphSchemaTest.cpp b/packages/nodegraph/tests/common/NodeGraphSchemaTest.cpp index a317b235..1c484505 100644 --- a/packages/nodegraph/tests/common/NodeGraphSchemaTest.cpp +++ b/packages/nodegraph/tests/common/NodeGraphSchemaTest.cpp @@ -192,6 +192,14 @@ TEST(NodeGraph, SchemaBasic) { } } + int32_t tick = 0; + for (int32_t j = 0; j < 10; j++) { + for (int32_t i = 0; i < 10; i++) { + ng.Tick(tick++, 0.001f); + ng.ProcessSubGraph(100); + } + } + for (auto nodeId : nodeIds) { TEST_TRUE(ng.RemoveNode(nodeId), ""); } From cd9d36820923d7f4bcf9755c6943b7355c99a20b Mon Sep 17 00:00:00 2001 From: lnd3 Date: Mon, 16 Sep 2024 21:42:47 +0200 Subject: [PATCH 21/42] Adjust rwa default convergence. --- packages/audio/include/audio/AudioUtils.h | 2 +- .../include/nodegraph/operations/NodeGraphOpControl.h | 4 ++-- .../source/common/operations/NodeGraphOpControl.cpp | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/audio/include/audio/AudioUtils.h b/packages/audio/include/audio/AudioUtils.h index ca9b13a6..d580cdc7 100644 --- a/packages/audio/include/audio/AudioUtils.h +++ b/packages/audio/include/audio/AudioUtils.h @@ -44,7 +44,7 @@ namespace l::audio { return false; } - FilterRWA& SetConvergenceInMs(T convergenceInMS, T limit = static_cast(0.0001)) { + FilterRWA& SetConvergenceInMs(T convergenceInMS, T limit = static_cast(0.001)) { mSmooth = GetRWAFactorFromMS(convergenceInMS, limit); return *this; } diff --git a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpControl.h b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpControl.h index ccdf2841..2879a38a 100644 --- a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpControl.h +++ b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpControl.h @@ -36,8 +36,8 @@ namespace l::nodegraph { mNodeInputManager.AddInputBase(InputTypeBase::CONSTANT_VALUE, AddInput("Rate", 256.0f, 1, 1.0f, 2048.0f)); mNodeInputManager.AddInputBase(InputTypeBase::INTERPOLATED, AddInput("Velocity", 0.5f, 1, 0.0f, 1.0f)); mNodeInputManager.AddInputBase(InputTypeBase::INTERPOLATED, AddInput("Fade", 0.1f, 1, 0.0001f, 1.0f)); - mNodeInputManager.AddInputBase(InputTypeBase::INTERPOLATED, AddInput("Attack", 50.0f, 1, 1.0f, 10000.0f)); - mNodeInputManager.AddInputBase(InputTypeBase::INTERPOLATED, AddInput("Release", 50.0f, 1, 1.0f, 10000.0f)); + mNodeInputManager.AddInputBase(InputTypeBase::CONSTANT_VALUE, AddInput("Attack", 50.0f, 1, 1.0f, 10000.0f)); + mNodeInputManager.AddInputBase(InputTypeBase::CONSTANT_VALUE, AddInput("Release", 50.0f, 1, 1.0f, 10000.0f)); AddOutput("Freq"); AddOutput("Volume"); diff --git a/packages/nodegraph/source/common/operations/NodeGraphOpControl.cpp b/packages/nodegraph/source/common/operations/NodeGraphOpControl.cpp index 216897b4..83cd9392 100644 --- a/packages/nodegraph/source/common/operations/NodeGraphOpControl.cpp +++ b/packages/nodegraph/source/common/operations/NodeGraphOpControl.cpp @@ -48,8 +48,8 @@ namespace l::nodegraph { inputManager.SetTarget(mFreqId, mFreqTarget); mAttackFrames = static_cast(inputManager.GetValue(4)); mReleaseFrames = static_cast(inputManager.GetValue(5)); - mAttackFactor = l::audio::GetRWAFactorFromTicks(inputManager.GetValueNext(4), 0.001f); - mReleaseFactor = l::audio::GetRWAFactorFromTicks(inputManager.GetValueNext(5), 0.001f); + mAttackFactor = l::audio::GetRWAFactorFromTicks(inputManager.GetValueNext(4), 0.01f); + mReleaseFactor = l::audio::GetRWAFactorFromTicks(inputManager.GetValueNext(5), 0.01f); } std::pair GraphControlEnvelope::ProcessSignal(NodeInputManager& inputManager) { @@ -108,7 +108,7 @@ namespace l::nodegraph { // note off } - return { freq, l::math::functions::pow(mEnvelope, 0.5f) }; + return { freq, mEnvelope }; } /*********************************************************************/ From 4144a130b5736f481f4309526e632eeb209c2f50 Mon Sep 17 00:00:00 2001 From: lnd3 Date: Tue, 17 Sep 2024 01:47:07 +0200 Subject: [PATCH 22/42] Fix rwa factor alternative when averaging away from zero to get proper attack interpolation. --- packages/audio/include/audio/AudioUtils.h | 9 +++++-- packages/audio/source/common/AudioUtils.cpp | 25 +++++++++++++++---- packages/hid/source/windows/MidiWindows.cpp | 4 +-- .../common/operations/NodeGraphOpControl.cpp | 8 +++--- 4 files changed, 33 insertions(+), 13 deletions(-) diff --git a/packages/audio/include/audio/AudioUtils.h b/packages/audio/include/audio/AudioUtils.h index d580cdc7..70b20d7e 100644 --- a/packages/audio/include/audio/AudioUtils.h +++ b/packages/audio/include/audio/AudioUtils.h @@ -20,8 +20,13 @@ namespace l::audio { float GetFrequencyFromNote(float note); double GetPhaseModifier(double note, double modifier); - float GetRWAFactorFromMS(float numMS, float limit, float rate = 44100.0f); - float GetRWAFactorFromTicks(float numTicks, float limit); + int32_t GetSamplesFromMS(float ms, float rate = 44100.0f); + float GetMSFromSamples(float numSamples, float rate = 44100.0f); + + float GetRWAFactorFromMS(float numMS, float limit = 0.0001f, float rate = 44100.0f); + float GetRWAFactorFromMSAttackSkew(float ms, float limit, float rate = 44100.0f); + + float GetRWAFactorFromSamples(int32_t numSamples, float limit = 0.0001f); float BatchUpdate(float updateSamples, float samplesLeft, int32_t start, int32_t end, std::function update, std::function process); diff --git a/packages/audio/source/common/AudioUtils.cpp b/packages/audio/source/common/AudioUtils.cpp index 6f9e13c6..eb0915bc 100644 --- a/packages/audio/source/common/AudioUtils.cpp +++ b/packages/audio/source/common/AudioUtils.cpp @@ -20,14 +20,29 @@ namespace l::audio { return 800.0 * modifier * modifier * limit; } + int32_t GetSamplesFromMS(float ms, float rate) { + return static_cast(ms * rate / 1000.0f); + } + + float GetMSFromSamples(float numSamples, float rate) { + return numSamples * 1000.0f / rate; + } + float GetRWAFactorFromMS(float ms, float limit, float rate) { - float ticks = ms * rate / 1000.0f; - return GetRWAFactorFromTicks(ticks, limit); + int32_t samples = static_cast(ms * rate / 1000.0f); + return GetRWAFactorFromSamples(samples, limit); + } + + float GetRWAFactorFromMSAttackSkew(float ms, float limit, float rate) { + float msRoot = l::math::functions::sqrt(ms); + int32_t samples = static_cast(msRoot * rate / 1000.0f); + float factor = l::audio::GetRWAFactorFromSamples(samples, limit); + factor *= factor; + return factor; } - float GetRWAFactorFromTicks(float numTicks, float limit) { - // rwa factor = pow(e, log(limit)/ticks) - return 1.0f - l::math::functions::pow(l::math::constants::E_f, l::math::functions::log(limit) / numTicks); + float GetRWAFactorFromSamples(int32_t numSamples, float limit) { + return 1.0f - l::math::functions::pow(l::math::constants::E_f, l::math::functions::log(limit) / static_cast(numSamples)); } float BatchUpdate(float updateSamples, float samplesLeft, int32_t start, int32_t end, std::function update, std::function process) { diff --git a/packages/hid/source/windows/MidiWindows.cpp b/packages/hid/source/windows/MidiWindows.cpp index 628ccdc5..7e3b18bb 100644 --- a/packages/hid/source/windows/MidiWindows.cpp +++ b/packages/hid/source/windows/MidiWindows.cpp @@ -302,8 +302,8 @@ namespace l::hid::midi { ClearCallbacks(); mMidiDevice.initDevices(); - RegisterCallback([](const MidiData& data) { - LOG(LogInfo) << "midi cb: device in:" << data.deviceIn << " device out:" << data.deviceOut << " stat:" << data.status << " ch:" << data.channel << " d1:" << data.data1 << " d2:" << data.data2; + RegisterCallback([](const MidiData&) { + //LOG(LogInfo) << "midi cb: device in:" << data.deviceIn << " device out:" << data.deviceOut << " stat:" << data.status << " ch:" << data.channel << " d1:" << data.data1 << " d2:" << data.data2; }); } diff --git a/packages/nodegraph/source/common/operations/NodeGraphOpControl.cpp b/packages/nodegraph/source/common/operations/NodeGraphOpControl.cpp index 83cd9392..f9c96b94 100644 --- a/packages/nodegraph/source/common/operations/NodeGraphOpControl.cpp +++ b/packages/nodegraph/source/common/operations/NodeGraphOpControl.cpp @@ -46,10 +46,10 @@ namespace l::nodegraph { mFreqTarget = inputManager.GetValueNext(mFreqTargetId); inputManager.SetTarget(mFreqId, mFreqTarget); - mAttackFrames = static_cast(inputManager.GetValue(4)); - mReleaseFrames = static_cast(inputManager.GetValue(5)); - mAttackFactor = l::audio::GetRWAFactorFromTicks(inputManager.GetValueNext(4), 0.01f); - mReleaseFactor = l::audio::GetRWAFactorFromTicks(inputManager.GetValueNext(5), 0.01f); + mAttackFrames = l::audio::GetSamplesFromMS(inputManager.GetValue(4)); + mReleaseFrames = l::audio::GetSamplesFromMS(inputManager.GetValue(5)); + mAttackFactor = l::audio::GetRWAFactorFromMSAttackSkew(inputManager.GetValue(4), 0.0001f); + mReleaseFactor = l::audio::GetRWAFactorFromMS(inputManager.GetValue(5), 0.0001f); } std::pair GraphControlEnvelope::ProcessSignal(NodeInputManager& inputManager) { From 6d66ad9eab7890840d7908a4145633a18dd1e715 Mon Sep 17 00:00:00 2001 From: lnd3 Date: Tue, 17 Sep 2024 01:59:35 +0200 Subject: [PATCH 23/42] Fix node graph plot. --- .../include/nodegraph/operations/NodeGraphOpOutput.h | 7 +++++-- packages/nodegraph/source/common/core/NodeGraphInput.cpp | 2 +- .../source/common/operations/NodeGraphOpOutput.cpp | 5 +++-- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpOutput.h b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpOutput.h index 61e0b79c..6ffd97d0 100644 --- a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpOutput.h +++ b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpOutput.h @@ -75,15 +75,18 @@ namespace l::nodegraph { public: GraphOutputPlot(NodeGraphBase* node, int32_t plotSamples) : NodeGraphOp(node, "Plot"), - mPlotSamples(plotSamples) + mPlotSamples(plotSamples), + mNodeInputManager(*this) { - AddInput("Plot", 0.0f, 1, -1.0f, 1.0f); + mNodeInputManager.AddInputBase(InputTypeBase::SAMPLED, AddInput("Plot", 0.0f, 1, -1.0f, 1.0f)); AddOutput("Plot", 0.0f, mPlotSamples); } virtual ~GraphOutputPlot() = default; virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; protected: + NodeInputManager mNodeInputManager; + int32_t mPlotSamples = 50; int32_t mCurIndex = 0; }; diff --git a/packages/nodegraph/source/common/core/NodeGraphInput.cpp b/packages/nodegraph/source/common/core/NodeGraphInput.cpp index e8ee7c65..15fc0ce7 100644 --- a/packages/nodegraph/source/common/core/NodeGraphInput.cpp +++ b/packages/nodegraph/source/common/core/NodeGraphInput.cpp @@ -73,7 +73,7 @@ namespace l::nodegraph { if (size > 1) { ASSERT(size == numSamples); } - return NodeInputDataIterator(&Get(numSamples), size); + return NodeInputDataIterator(&Get(size), size); } NodeInputDataIterator NodeGraphInput::GetArrayIterator() { diff --git a/packages/nodegraph/source/common/operations/NodeGraphOpOutput.cpp b/packages/nodegraph/source/common/operations/NodeGraphOpOutput.cpp index b2a2a4b3..d2037ad7 100644 --- a/packages/nodegraph/source/common/operations/NodeGraphOpOutput.cpp +++ b/packages/nodegraph/source/common/operations/NodeGraphOpOutput.cpp @@ -84,12 +84,13 @@ namespace l::nodegraph { /*********************************************************************/ void GraphOutputPlot::Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) { - float* value = &inputs.at(0).Get(numSamples); + mNodeInputManager.ProcessUpdate(inputs, numSamples); + int32_t outputSize = outputs.at(0).GetSize(); float* output = &outputs.at(0).Get(outputSize); for (int32_t i = 0; i < numSamples; i++) { - output[mCurIndex] = *value++; + output[mCurIndex] = mNodeInputManager.GetValueNext(0); mCurIndex = (mCurIndex + 1) % outputSize; } } From 7c8b5cc13aef834c69b26c2c0b0b234b59725900 Mon Sep 17 00:00:00 2001 From: lnd3 Date: Wed, 18 Sep 2024 02:44:29 +0200 Subject: [PATCH 24/42] Clean up some of the new crap. Add a tween facility for simple s curve smoothing. --- packages/audio/include/audio/AudioUtils.h | 50 +++-- packages/audio/source/common/AudioUtils.cpp | 31 ++- packages/math/include/math/MathAll.h | 1 + packages/math/include/math/MathTween.h | 55 ++++++ packages/math/source/common/MathTween.cpp | 71 +++++++ .../include/nodegraph/NodeGraphSchema.h | 10 +- .../include/nodegraph/core/NodeGraphBase.h | 74 ++------ .../include/nodegraph/core/NodeGraphInput.h | 172 ++++------------- .../nodegraph/operations/NodeGraphOpControl.h | 6 +- .../nodegraph/operations/NodeGraphOpFilter.h | 2 +- .../nodegraph/operations/NodeGraphOpOutput.h | 2 +- .../nodegraph/operations/NodeGraphOpSignal.h | 17 +- .../source/common/core/NodeGraphBase.cpp | 59 ++++++ .../source/common/core/NodeGraphInput.cpp | 176 ++++++++++++++++++ .../common/operations/NodeGraphOpControl.cpp | 26 +-- .../common/operations/NodeGraphOpEffect.cpp | 12 +- .../common/operations/NodeGraphOpFilter.cpp | 10 +- .../common/operations/NodeGraphOpOutput.cpp | 7 +- .../common/operations/NodeGraphOpSignal.cpp | 55 ++++-- 19 files changed, 563 insertions(+), 273 deletions(-) create mode 100644 packages/math/include/math/MathTween.h create mode 100644 packages/math/source/common/MathTween.cpp diff --git a/packages/audio/include/audio/AudioUtils.h b/packages/audio/include/audio/AudioUtils.h index 70b20d7e..a087fa0b 100644 --- a/packages/audio/include/audio/AudioUtils.h +++ b/packages/audio/include/audio/AudioUtils.h @@ -1,6 +1,6 @@ #pragma once -#include "math/MathFunc.h" +#include "math/MathAll.h" #include @@ -20,15 +20,14 @@ namespace l::audio { float GetFrequencyFromNote(float note); double GetPhaseModifier(double note, double modifier); - int32_t GetSamplesFromMS(float ms, float rate = 44100.0f); - float GetMSFromSamples(float numSamples, float rate = 44100.0f); + int32_t GetAudioTicksFromMS(float ms, float sampleRate = 44100.0f); + float GetMSFromAudioTicks(float numTicks, float sampleRate = 44100.0f); - float GetRWAFactorFromMS(float numMS, float limit = 0.0001f, float rate = 44100.0f); - float GetRWAFactorFromMSAttackSkew(float ms, float limit, float rate = 44100.0f); + float GetRWAFactorFromMS(float numMS, float limit = 0.0001f, float rwaUpdateRate = 1.0f, float sampleRate = 44100.0f); + float GetRWAFactorFromMSSkewed(float ms, float limit = 0.0001f, float rwaUpdateRate = 1.0f, float sampleRate = 44100.0f); - float GetRWAFactorFromSamples(int32_t numSamples, float limit = 0.0001f); - float BatchUpdate(float updateSamples, float samplesLeft, int32_t start, int32_t end, std::function update, std::function process); + float BatchUpdate(float updateSamples, float samplesLeft, int32_t start, int32_t end, std::function update, std::function process); template class FilterRWA { @@ -36,8 +35,10 @@ namespace l::audio { FilterRWA() : mSmooth(static_cast(0.005)), + mSmoothSkewed(mSmooth * mSmooth), mValue(static_cast(0)), - mTargetValue(static_cast(0)) + mTargetValue(static_cast(0)), + mRWAUpdateRate(static_cast(1.0)) {} ~FilterRWA() = default; @@ -49,13 +50,26 @@ namespace l::audio { return false; } - FilterRWA& SetConvergenceInMs(T convergenceInMS, T limit = static_cast(0.001)) { - mSmooth = GetRWAFactorFromMS(convergenceInMS, limit); + FilterRWA& SetConvergenceInMs(T convergenceInMS, T limit = static_cast(0.0001), T sampleRate = static_cast(44100.0)) { + mSmooth = GetRWAFactorFromMS(convergenceInMS, limit, mRWAUpdateRate, sampleRate); + mSmoothSkewed = GetRWAFactorFromMSSkewed(convergenceInMS, limit, mRWAUpdateRate, sampleRate); return *this; } - FilterRWA& SetConvergence(T smooth = static_cast(0.005)) { + FilterRWA& SetConvergenceFactor(T smooth = static_cast(0.005)) { mSmooth = smooth; + mSmoothSkewed = mSmooth * mSmooth; + return *this; + } + + FilterRWA& SetConvergenceInTicks(T ticks, T limit = static_cast(0.001)) { + mSmooth = l::math::tween::GetRWAFactor(static_cast(ticks), limit); + mSmoothSkewed = mSmooth * mSmooth; + return *this; + } + + FilterRWA& SetRWAUpdateRate(T rwaUpdateRate = static_cast(1.0)) { + mRWAUpdateRate = l::math::functions::max(rwaUpdateRate, static_cast(1.0)); return *this; } @@ -64,6 +78,17 @@ namespace l::audio { return *this; } + T NextSkewed() { + float delta = mTargetValue - mValue; + if (delta < 0) { + mValue += mSmooth * delta; + } + else { + mValue += mSmoothSkewed * delta; + } + return mValue; + } + T Next() { mValue += mSmooth * (mTargetValue - mValue); return mValue; @@ -75,9 +100,12 @@ namespace l::audio { protected: T mSmooth; + T mSmoothSkewed; T mValue; T mTargetValue; + T mRWAUpdateRate; }; using FilterRWAFloat = FilterRWA; + } diff --git a/packages/audio/source/common/AudioUtils.cpp b/packages/audio/source/common/AudioUtils.cpp index eb0915bc..8fd5c240 100644 --- a/packages/audio/source/common/AudioUtils.cpp +++ b/packages/audio/source/common/AudioUtils.cpp @@ -4,6 +4,7 @@ #include "math/MathAll.h" #include "math/MathFunc.h" +#include "math/MathTween.h" #include @@ -20,39 +21,36 @@ namespace l::audio { return 800.0 * modifier * modifier * limit; } - int32_t GetSamplesFromMS(float ms, float rate) { - return static_cast(ms * rate / 1000.0f); + int32_t GetAudioTicksFromMS(float ms, float sampleRate) { + return static_cast(ms * sampleRate / 1000.0f); } - float GetMSFromSamples(float numSamples, float rate) { - return numSamples * 1000.0f / rate; + float GetMSFromAudioTicks(float numAudioSamples, float sampleRate) { + return numAudioSamples * 1000.0f / sampleRate; } - float GetRWAFactorFromMS(float ms, float limit, float rate) { - int32_t samples = static_cast(ms * rate / 1000.0f); - return GetRWAFactorFromSamples(samples, limit); + float GetRWAFactorFromMS(float ms, float limit, float rwaUpdateRate, float sampleRate) { + int32_t updateSteps = GetAudioTicksFromMS(ms, sampleRate / rwaUpdateRate); + return l::math::tween::GetRWAFactor(updateSteps, limit); } - float GetRWAFactorFromMSAttackSkew(float ms, float limit, float rate) { + float GetRWAFactorFromMSSkewed(float ms, float limit, float rwaUpdateRate, float sampleRate) { float msRoot = l::math::functions::sqrt(ms); - int32_t samples = static_cast(msRoot * rate / 1000.0f); - float factor = l::audio::GetRWAFactorFromSamples(samples, limit); + int32_t steps = GetAudioTicksFromMS(msRoot, sampleRate / rwaUpdateRate); + float factor = l::math::tween::GetRWAFactor(steps, limit); factor *= factor; return factor; } - float GetRWAFactorFromSamples(int32_t numSamples, float limit) { - return 1.0f - l::math::functions::pow(l::math::constants::E_f, l::math::functions::log(limit) / static_cast(numSamples)); - } - - float BatchUpdate(float updateSamples, float samplesLeft, int32_t start, int32_t end, std::function update, std::function process) { + float BatchUpdate(float updateSamples, float samplesLeft, int32_t start, int32_t end, std::function update, std::function process) { float startNum = static_cast(start); while (startNum < static_cast(end)) { bool updated = false; if (samplesLeft < 1.0f) { samplesLeft += updateSamples; if (update != nullptr) { - update(); + float rate = update(); + updateSamples = rate > 0.0f ? rate : updateSamples; } updated = true; } @@ -71,5 +69,4 @@ namespace l::audio { } return samplesLeft; } - } diff --git a/packages/math/include/math/MathAll.h b/packages/math/include/math/MathAll.h index 3bb505b4..abbee162 100644 --- a/packages/math/include/math/MathAll.h +++ b/packages/math/include/math/MathAll.h @@ -4,3 +4,4 @@ #include "math/MathSmooth.h" #include "math/MathConstants.h" #include "math/MathAlgorithm.h" +#include "math/MathTween.h" diff --git a/packages/math/include/math/MathTween.h b/packages/math/include/math/MathTween.h new file mode 100644 index 00000000..e642c8c6 --- /dev/null +++ b/packages/math/include/math/MathTween.h @@ -0,0 +1,55 @@ +#pragma once + +#include +#include +#include +#include +#include + +namespace l::math::tween { + + float GetRWAFactor(int32_t steps, float limit); + + class RecentWeightedAverage { + public: + RecentWeightedAverage() = default; + ~RecentWeightedAverage() = default; + + void SetConvergence(float smooth); + void SetTarget(float target); + float Next(); + float Value(); + protected: + float mSmooth; + float mValue; + float mTargetValue; + }; + + /**********************************************************************************/ + + class DynamicTween { + public: + DynamicTween(int32_t updateCount = 0, float dynamicSmooth = 0.25f) : + mUpdateCount(updateCount), + mDynamicSmooth(dynamicSmooth) + {} + ~DynamicTween() = default; + + void Reset(float value = 0.0f, bool ignoreDynamicSmooth = false); + void SetTweenLength(int32_t tweenCount); + void SetTarget(float target, int32_t tweenCount = 0); + void Update(); + float Next(); + float Value(); + + protected: + int32_t mUpdateCount = 1; + float mDynamicSmooth = 0.25f; + + float mValue = 0.0f; + float mTarget = 0.0f; + float mTargetValue = 0.0f; + float mTargetValuePrev = 0.0f; + int32_t mCounter = 0; + }; +} diff --git a/packages/math/source/common/MathTween.cpp b/packages/math/source/common/MathTween.cpp new file mode 100644 index 00000000..71f64b6b --- /dev/null +++ b/packages/math/source/common/MathTween.cpp @@ -0,0 +1,71 @@ + +#include "math/MathTween.h" +#include "math/MathSmooth.h" +#include "math/MathFunc.h" + +namespace l::math::tween { + + float GetRWAFactor(int32_t steps, float limit) { + return 1.0f - l::math::functions::pow(l::math::constants::E_f, l::math::functions::log(limit) / static_cast(steps)); + } + + void RecentWeightedAverage::SetConvergence(float smooth) { + mSmooth = smooth; + } + + void RecentWeightedAverage::SetTarget(float target) { + mTargetValue = target; + } + + float RecentWeightedAverage::Next() { + mValue += mSmooth * (mTargetValue - mValue); + return mValue; + } + + float RecentWeightedAverage::Value() { + return mValue; + } + + /**********************************************************************************/ + + void DynamicTween::Reset(float value, bool ignoreDynamicSmooth) { + mTargetValue = value; + mTargetValuePrev = value; + if (ignoreDynamicSmooth) { + mValue = value; + } + mCounter = 0; + } + + void DynamicTween::SetTweenLength(int32_t tweenCount) { + mUpdateCount = l::math::functions::max(tweenCount, 4); + } + + void DynamicTween::SetTarget(float target, int32_t tweenCount) { + mTargetValuePrev = mValue; + mTargetValue = target; + mCounter = 0; + mUpdateCount = l::math::functions::max(tweenCount, 4); + } + + // update rate in audio processing, the number of samples between calls + // we can use this to update expensive interpolation here + void DynamicTween::Update() { + mTarget = mTargetValue; + if (mCounter < mUpdateCount) { + float t = mCounter / static_cast(mUpdateCount); + mTarget = mTargetValuePrev + l::math::smooth::smoothPolyh3(t) * (mTargetValue - mTargetValuePrev); + } + } + + float DynamicTween::Next() { + mValue += mDynamicSmooth * (mTarget - mValue); + mCounter++; + return mValue; + } + + float DynamicTween::Value() { + mValue += mDynamicSmooth * (mTarget - mValue); + return mValue; + } +} diff --git a/packages/nodegraph/include/nodegraph/NodeGraphSchema.h b/packages/nodegraph/include/nodegraph/NodeGraphSchema.h index ab6f89ba..3c82c6ff 100644 --- a/packages/nodegraph/include/nodegraph/NodeGraphSchema.h +++ b/packages/nodegraph/include/nodegraph/NodeGraphSchema.h @@ -45,6 +45,7 @@ namespace l::nodegraph { RegisterNodeType("Source", 2, "Value [0,100]"); RegisterNodeType("Source", 3, "Value [-inf,inf]"); RegisterNodeType("Source", 4, "Time"); + RegisterNodeType("Numeric", 50, "Add"); RegisterNodeType("Numeric", 51, "Subtract"); RegisterNodeType("Numeric", 52, "Negate"); @@ -53,23 +54,26 @@ namespace l::nodegraph { RegisterNodeType("Numeric", 55, "Multiply3"); RegisterNodeType("Numeric", 56, "Multiply & Add"); RegisterNodeType("Numeric", 57, "Round"); + RegisterNodeType("Logic", 100, "And"); RegisterNodeType("Logic", 101, "Or"); RegisterNodeType("Logic", 102, "Xor"); + RegisterNodeType("Filter", 150, "Lowpass"); RegisterNodeType("Filter", 151, "Highpass"); RegisterNodeType("Filter", 152, "Chamberlin two-pole (4 mode)"); + RegisterNodeType("Output", 200, "Debug"); RegisterNodeType("Output", 201, "Speaker"); RegisterNodeType("Output", 202, "Plot"); - RegisterNodeType("Effect", 250, "Envelope"); + RegisterNodeType("Effect", 251, "Reverb1"); RegisterNodeType("Effect", 252, "Reverb2"); - //RegisterNodeType("Effect", 253, "Reverb2"); RegisterNodeType("Effect", 254, "Limiter"); RegisterNodeType("Effect", 255, "Envelope Follower"); RegisterNodeType("Effect", 256, "Saturator"); RegisterNodeType("Effect", 257, "Trance Gate"); + RegisterNodeType("Input", 300, "Keyboard Piano"); RegisterNodeType("Input", 301, "Midi Keyboard"); RegisterNodeType("Input", 302, "Midi Knobs"); @@ -78,6 +82,7 @@ namespace l::nodegraph { RegisterNodeType("Input", 305, "Midi Button Group 3"); RegisterNodeType("Input", 306, "Midi Button Group 4"); RegisterNodeType("Input", 307, "Midi Button Group 5"); + RegisterNodeType("Signal", 350, "Sine"); RegisterNodeType("Signal", 351, "Sine FM 1"); RegisterNodeType("Signal", 352, "Sine FM 2"); @@ -85,6 +90,7 @@ namespace l::nodegraph { RegisterNodeType("Signal", 354, "Saw"); RegisterNodeType("Signal", 355, "Sine 2"); RegisterNodeType("Signal", 356, "Saw 2"); + RegisterNodeType("Control", 400, "Envelope"); RegisterNodeType("Control", 401, "Arpeggio"); diff --git a/packages/nodegraph/include/nodegraph/core/NodeGraphBase.h b/packages/nodegraph/include/nodegraph/core/NodeGraphBase.h index 325d32dc..f2a70a8e 100644 --- a/packages/nodegraph/include/nodegraph/core/NodeGraphBase.h +++ b/packages/nodegraph/include/nodegraph/core/NodeGraphBase.h @@ -27,6 +27,8 @@ namespace l::nodegraph { class NodeGraphGroup; + + /**********************************************************************************/ class NodeGraphBase { public: NodeGraphBase(OutputType outputType) : mId(CreateUniqueId()), mOutputType(outputType) { @@ -95,6 +97,8 @@ namespace l::nodegraph { std::string mName; }; + /**********************************************************************************/ + class NodeGraphOp { public: NodeGraphOp(NodeGraphBase* node, std::string_view name) : @@ -137,6 +141,8 @@ namespace l::nodegraph { int8_t mNumOutputs = 0; }; + /**********************************************************************************/ + template class NodeGraph : public NodeGraphBase { public: @@ -214,7 +220,7 @@ namespace l::nodegraph { T mOperation; }; - + /**********************************************************************************/ class NodeInputManager { public: @@ -224,64 +230,20 @@ namespace l::nodegraph { } ~NodeInputManager() = default; - int32_t AddInputBase(InputTypeBase type, int32_t inputIndex = -1) { - if (type != InputTypeBase::CUSTOM_VALUE_INTERP_MS) { - ASSERT(inputIndex >= 0); - } - - inputIndex = static_cast(mInputs.size()); - mInputs.emplace_back(NodeInput{ type, inputIndex }); - - if (type != InputTypeBase::CUSTOM_VALUE_INTERP_MS) { - mInputs.back().SetValue(mNodeGraphOperation.GetDefaultData(static_cast(inputIndex))); - mInputs.back().SetTarget(mNodeGraphOperation.GetDefaultData(static_cast(inputIndex))); - } - return inputIndex; - } - - void ProcessUpdate(std::vector& inputs, int32_t numSamples) { - for (auto& input : mInputs) { - input.ProcessUpdate(inputs, numSamples); - } - } - - void NodeUpdate(std::vector& inputs) { - for (auto& input : mInputs) { - input.NodeUpdate(inputs); - } - } - - float GetValueNext(int32_t inputIndex) { - return mInputs.at(inputIndex).GetValueNext(); - } - - float GetValue(int32_t inputIndex) { - return mInputs.at(inputIndex).GetValue(); - } - - float GetArrayValue(int32_t inputIndex, int32_t arrayIndex) { - return mInputs.at(inputIndex).GetArrayValue(arrayIndex); - } - - float* GetArray(int32_t inputIndex) { - return mInputs.at(inputIndex).GetArray(); - } - - void SetConvergence(int32_t inputIndex, float value) { - mInputs.at(inputIndex).SetConvergence(value); - } - - void SetTarget(int32_t inputIndex, float value) { - mInputs.at(inputIndex).SetTarget(value); - } - - void SetValue(int32_t inputIndex, float value) { - mInputs.at(inputIndex).SetValue(value); - } + int32_t AddInputBase(InputTypeBase type, int32_t inputIndex = -1); + void ProcessUpdate(std::vector& inputs, int32_t numSamples, float updateRate); + void NodeUpdate(std::vector& inputs, float updateRate); + float GetValueNext(int32_t inputIndex); + float GetValue(int32_t inputIndex); + float GetArrayValue(int32_t inputIndex, int32_t arrayIndex); + float* GetArray(int32_t inputIndex); + void SetDuration(int32_t inputIndex, float value); + void SetTarget(int32_t inputIndex, float value); + void SetValue(int32_t inputIndex, float value); protected: NodeGraphOp& mNodeGraphOperation; - std::vector mInputs; + std::vector mInputs; //std::vector mInputsRefs; }; } diff --git a/packages/nodegraph/include/nodegraph/core/NodeGraphInput.h b/packages/nodegraph/include/nodegraph/core/NodeGraphInput.h index 8214ded7..8ce92738 100644 --- a/packages/nodegraph/include/nodegraph/core/NodeGraphInput.h +++ b/packages/nodegraph/include/nodegraph/core/NodeGraphInput.h @@ -9,7 +9,7 @@ #include #include -#include "math/MathConstants.h" +#include "math/MathAll.h" #include "audio/AudioUtils.h" namespace l::nodegraph { @@ -41,6 +41,8 @@ namespace l::nodegraph { float mInputFloatConstant; }; + /*********************************************************************************/ + class NodeInputDataIterator { public: NodeInputDataIterator(float* data = nullptr, int32_t size = 0) { @@ -76,6 +78,8 @@ namespace l::nodegraph { int32_t mIncrement = 0; }; + /*********************************************************************************/ + class NodeGraphInput { public: Input mInput; @@ -102,36 +106,45 @@ namespace l::nodegraph { /****************************************************************************************/ enum class InputTypeBase { SAMPLED = 0, - INTERPOLATED, - INTERPOLATED_MS, + INTERP_RWA, + INTERP_RWA_MS, + INTERP_TWEEN, + INTERP_TWEEN_MS, CONSTANT_VALUE, CONSTANT_ARRAY, - CUSTOM_VALUE_INTERP_MS + CUSTOM_VALUE_INTERP_RWA_MS }; union InputUnion { - l::audio::FilterRWAFloat mFilter; + l::audio::FilterRWAFloat mFilterRWA; + l::math::tween::DynamicTween mTween; NodeInputDataIterator mIterator; - InputUnion() : mFilter() {} + InputUnion() : mFilterRWA() {} ~InputUnion() = default; }; - class NodeInput { + /*********************************************************************************/ + + class NodeGraphInputAccessor { public: - NodeInput(InputTypeBase type, int32_t inputIndex) : + NodeGraphInputAccessor(InputTypeBase type, int32_t inputIndex) : mType(type), mInputIndex(inputIndex) { switch(mType) { - case InputTypeBase::INTERPOLATED: - case InputTypeBase::INTERPOLATED_MS: - case InputTypeBase::CUSTOM_VALUE_INTERP_MS: + case InputTypeBase::INTERP_RWA: + case InputTypeBase::INTERP_RWA_MS: + case InputTypeBase::CUSTOM_VALUE_INTERP_RWA_MS: //new (&s.vec) std::vector; - new (&mInput.mFilter) l::audio::FilterRWA(); + new (&mInput.mFilterRWA) l::audio::FilterRWA(); //mInput.mFilter = l::audio::FilterRWA(); break; + case InputTypeBase::INTERP_TWEEN: + case InputTypeBase::INTERP_TWEEN_MS: + new (&mInput.mTween) l::math::tween::DynamicTween(); + break; case InputTypeBase::SAMPLED: case InputTypeBase::CONSTANT_VALUE: case InputTypeBase::CONSTANT_ARRAY: @@ -140,129 +153,18 @@ namespace l::nodegraph { break; } } - ~NodeInput() = default; - - void SetConvergence(float value) { - switch(mType) { - case InputTypeBase::INTERPOLATED: - mInput.mFilter.SetConvergence(value); - break; - case InputTypeBase::INTERPOLATED_MS: - case InputTypeBase::CUSTOM_VALUE_INTERP_MS: - mInput.mFilter.SetConvergenceInMs(value); - break; - case InputTypeBase::SAMPLED: - case InputTypeBase::CONSTANT_VALUE: - case InputTypeBase::CONSTANT_ARRAY: - ASSERT(false) << "Failed to set convergence on a non interpolated input type"; - break; - } - } - - void SetTarget(float value) { - switch (mType) { - case InputTypeBase::INTERPOLATED: - case InputTypeBase::INTERPOLATED_MS: - case InputTypeBase::CUSTOM_VALUE_INTERP_MS: - mInput.mFilter.SetTarget(value); - break; - case InputTypeBase::SAMPLED: - case InputTypeBase::CONSTANT_VALUE: - case InputTypeBase::CONSTANT_ARRAY: - break; - } - } - - void SetValue(float value) { - switch (mType) { - case InputTypeBase::INTERPOLATED: - case InputTypeBase::INTERPOLATED_MS: - case InputTypeBase::CUSTOM_VALUE_INTERP_MS: - mInput.mFilter.Value() = value; - break; - case InputTypeBase::SAMPLED: - case InputTypeBase::CONSTANT_VALUE: - case InputTypeBase::CONSTANT_ARRAY: - break; - } - } - - float GetValueNext() { - switch (mType) { - case InputTypeBase::INTERPOLATED: - case InputTypeBase::INTERPOLATED_MS: - case InputTypeBase::CUSTOM_VALUE_INTERP_MS: - return mInput.mFilter.Next(); - case InputTypeBase::SAMPLED: - case InputTypeBase::CONSTANT_ARRAY: - return *mInput.mIterator++; - case InputTypeBase::CONSTANT_VALUE: - return *mInput.mIterator; - } - return 0.0f; - } - - float GetValue() { - switch (mType) { - case InputTypeBase::INTERPOLATED: - case InputTypeBase::INTERPOLATED_MS: - case InputTypeBase::CUSTOM_VALUE_INTERP_MS: - return mInput.mFilter.Value(); - case InputTypeBase::SAMPLED: - case InputTypeBase::CONSTANT_VALUE: - case InputTypeBase::CONSTANT_ARRAY: - return *mInput.mIterator; - } - return 0.0f; - } - - float GetArrayValue(int32_t index) { - if (mType == InputTypeBase::CONSTANT_ARRAY) { - return mInput.mIterator[index]; - } - return 0.0f; - } - - float* GetArray() { - if (mType == InputTypeBase::CONSTANT_ARRAY) { - return mInput.mIterator.data(); - } - return nullptr; - } - - // run on each new batch call to setup input iterators for buffered data - void ProcessUpdate(std::vector& input, int32_t numSamples) { - switch (mType) { - case InputTypeBase::INTERPOLATED: - case InputTypeBase::INTERPOLATED_MS: - case InputTypeBase::CUSTOM_VALUE_INTERP_MS: - break; - case InputTypeBase::SAMPLED: - mInput.mIterator = input.at(mInputIndex).GetBufferIterator(numSamples); - break; - case InputTypeBase::CONSTANT_VALUE: - mInput.mIterator.Reset(&input.at(mInputIndex).Get(), 1); - break; - case InputTypeBase::CONSTANT_ARRAY: - mInput.mIterator = input.at(mInputIndex).GetArrayIterator(); - break; - } - } - - // run on each node update (can be node specific) and will update node rwa filters - void NodeUpdate(std::vector& input) { - switch (mType) { - case InputTypeBase::INTERPOLATED: - case InputTypeBase::INTERPOLATED_MS: - mInput.mFilter.SetTarget(input.at(mInputIndex).Get()); - break; - case InputTypeBase::CUSTOM_VALUE_INTERP_MS: // must set it manually - case InputTypeBase::SAMPLED: - case InputTypeBase::CONSTANT_VALUE: - case InputTypeBase::CONSTANT_ARRAY: - break; - } - } + ~NodeGraphInputAccessor() = default; + + void SetDuration(float ms); + void SetDuration(int32_t ticks); + void SetTarget(float value); + void SetValue(float value); + float GetValueNext(); + float GetValue(); + float GetArrayValue(int32_t index); + float* GetArray(); + void ProcessUpdate(std::vector& input, int32_t numSamples, float updateRate); + void NodeUpdate(std::vector& input, float updateRate); protected: InputTypeBase mType; diff --git a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpControl.h b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpControl.h index 2879a38a..b1d6df1d 100644 --- a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpControl.h +++ b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpControl.h @@ -34,8 +34,8 @@ namespace l::nodegraph { { mNodeInputManager.AddInputBase(InputTypeBase::CONSTANT_VALUE, AddInput("Sync", 0.0f, 1, 0.0f, 1.0f)); mNodeInputManager.AddInputBase(InputTypeBase::CONSTANT_VALUE, AddInput("Rate", 256.0f, 1, 1.0f, 2048.0f)); - mNodeInputManager.AddInputBase(InputTypeBase::INTERPOLATED, AddInput("Velocity", 0.5f, 1, 0.0f, 1.0f)); - mNodeInputManager.AddInputBase(InputTypeBase::INTERPOLATED, AddInput("Fade", 0.1f, 1, 0.0001f, 1.0f)); + mNodeInputManager.AddInputBase(InputTypeBase::INTERP_TWEEN, AddInput("Velocity", 0.5f, 1, 0.0f, 1.0f)); + mNodeInputManager.AddInputBase(InputTypeBase::INTERP_TWEEN, AddInput("Fade", 0.1f, 1, 0.0001f, 1.0f)); mNodeInputManager.AddInputBase(InputTypeBase::CONSTANT_VALUE, AddInput("Attack", 50.0f, 1, 1.0f, 10000.0f)); mNodeInputManager.AddInputBase(InputTypeBase::CONSTANT_VALUE, AddInput("Release", 50.0f, 1, 1.0f, 10000.0f)); @@ -65,7 +65,7 @@ namespace l::nodegraph { GraphControlBase(node, "Envelope") { mFreqTargetId = mNodeInputManager.AddInputBase(InputTypeBase::CONSTANT_VALUE, AddInput("Freq")); - mFreqId = mNodeInputManager.AddInputBase(InputTypeBase::CUSTOM_VALUE_INTERP_MS); + mFreqId = mNodeInputManager.AddInputBase(InputTypeBase::CUSTOM_VALUE_INTERP_RWA_MS); mEnvelope = 0.0f; } diff --git a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpFilter.h b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpFilter.h index eb0e533c..2b1277e4 100644 --- a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpFilter.h +++ b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpFilter.h @@ -54,7 +54,7 @@ namespace l::nodegraph { protected: float mReset = 0.0f; float mSamplesUntilUpdate = 0.0f; - float mUpdateSamples = 16.0f; + float mUpdateRate = 16.0f; float mCutoff = 0.0f; float mResonance = 0.0f; diff --git a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpOutput.h b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpOutput.h index 6ffd97d0..20f98131 100644 --- a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpOutput.h +++ b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpOutput.h @@ -54,7 +54,7 @@ namespace l::nodegraph { AddInput("Volume", 0.5f, 1, 0.0f, 1.0f); mEnvelope = 0.0f; - mFilterEnvelope.SetConvergence(); + mFilterEnvelope.SetConvergenceFactor(); } virtual ~GraphOutputSpeaker() = default; diff --git a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpSignal.h b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpSignal.h index 45505a00..1e5c17d9 100644 --- a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpSignal.h +++ b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpSignal.h @@ -29,13 +29,15 @@ namespace l::nodegraph { static const int8_t mNumDefaultOutputs = 1; GraphSignalBase(NodeGraphBase* node, std::string_view name) : - NodeGraphOp(node, name) + NodeGraphOp(node, name), + mNodeInputManager(*this) { - AddInput("Sync", 0.0f, 1, 0.0f, 1.0f); - AddInput("Rate", 256.0f, 1, 1.0f, 2048.0f); - AddInput("Freq", 0.0f, 1, 0.0f, 22050.0f); - AddInput("Volume", 0.5f, 1, 0.0f, 5.0f); - AddInput("Smooth", 1.0f, 1, 0.0f, 1.0f); + mNodeInputManager.AddInputBase(InputTypeBase::CONSTANT_VALUE, AddInput("Sync", 0.0f, 1, 0.0f, 1.0f)); + mNodeInputManager.AddInputBase(InputTypeBase::CONSTANT_VALUE, AddInput("Rate", 256.0f, 1, 1.0f, 2048.0f)); + mNodeInputManager.AddInputBase(InputTypeBase::INTERP_TWEEN, AddInput("Freq", 0.0f, 1, 0.0f, 22050.0f)); + mNodeInputManager.AddInputBase(InputTypeBase::INTERP_TWEEN, AddInput("Volume", 0.5f, 1, 0.0f, 5.0f)); + mNodeInputManager.AddInputBase(InputTypeBase::INTERP_TWEEN, AddInput("Smooth", 1.0f, 1, 0.0f, 1.0f)); + AddOutput("Out", 0.0f, 2); mFreq = 0.0f; @@ -55,6 +57,7 @@ namespace l::nodegraph { virtual void UpdateSignal(std::vector&, std::vector&) {}; virtual float ProcessSignal(float deltaTime, float freq) = 0; protected: + NodeInputManager mNodeInputManager; float mReset = 0.0f; float mFreq = 0.0f; @@ -62,7 +65,7 @@ namespace l::nodegraph { float mDeltaTime = 0.0f; float mVolumeTarget = 0.0f; float mSamplesUntilUpdate = 0.0f; - float mUpdateSamples = 16.0f; + float mUpdateRate = 16.0f; l::audio::FilterRWA mFilterSignal; l::audio::FilterRWA mFilterVolume; diff --git a/packages/nodegraph/source/common/core/NodeGraphBase.cpp b/packages/nodegraph/source/common/core/NodeGraphBase.cpp index 2f0c067a..66edb6f6 100644 --- a/packages/nodegraph/source/common/core/NodeGraphBase.cpp +++ b/packages/nodegraph/source/common/core/NodeGraphBase.cpp @@ -16,6 +16,8 @@ namespace l::nodegraph { return inoutNum >= 0 && inoutSize < 256u && inoutNum < static_cast(inoutSize); } + /**********************************************************************************/ + void NodeGraphBase::SetNumInputs(int8_t numInputs) { mInputs.resize(numInputs); } @@ -294,6 +296,8 @@ namespace l::nodegraph { } } + /**********************************************************************************/ + void NodeGraphOp::Reset() { for (int8_t i = 0; i < static_cast(mDefaultInData.size()); i++) { auto& e = mDefaultInData.at(i); @@ -366,5 +370,60 @@ namespace l::nodegraph { return static_cast(mDefaultInData.size() - 1); } + /**********************************************************************************/ + + int32_t NodeInputManager::AddInputBase(InputTypeBase type, int32_t inputIndex) { + if (type != InputTypeBase::CUSTOM_VALUE_INTERP_RWA_MS) { + ASSERT(inputIndex >= 0); + } + + inputIndex = static_cast(mInputs.size()); + mInputs.emplace_back(NodeGraphInputAccessor{ type, inputIndex }); + + if (type != InputTypeBase::CUSTOM_VALUE_INTERP_RWA_MS) { + mInputs.back().SetValue(mNodeGraphOperation.GetDefaultData(static_cast(inputIndex))); + mInputs.back().SetTarget(mNodeGraphOperation.GetDefaultData(static_cast(inputIndex))); + } + return inputIndex; + } + + void NodeInputManager::ProcessUpdate(std::vector& inputs, int32_t numSamples, float updateRate) { + for (auto& input : mInputs) { + input.ProcessUpdate(inputs, numSamples, updateRate); + } + } + + void NodeInputManager::NodeUpdate(std::vector& inputs, float updateRate) { + for (auto& input : mInputs) { + input.NodeUpdate(inputs, updateRate); + } + } + float NodeInputManager::GetValueNext(int32_t inputIndex) { + return mInputs.at(inputIndex).GetValueNext(); + } + + float NodeInputManager::GetValue(int32_t inputIndex) { + return mInputs.at(inputIndex).GetValue(); + } + + float NodeInputManager::GetArrayValue(int32_t inputIndex, int32_t arrayIndex) { + return mInputs.at(inputIndex).GetArrayValue(arrayIndex); + } + + float* NodeInputManager::GetArray(int32_t inputIndex) { + return mInputs.at(inputIndex).GetArray(); + } + + void NodeInputManager::SetDuration(int32_t inputIndex, float value) { + mInputs.at(inputIndex).SetDuration(value); + } + + void NodeInputManager::SetTarget(int32_t inputIndex, float value) { + mInputs.at(inputIndex).SetTarget(value); + } + + void NodeInputManager::SetValue(int32_t inputIndex, float value) { + mInputs.at(inputIndex).SetValue(value); + } } \ No newline at end of file diff --git a/packages/nodegraph/source/common/core/NodeGraphInput.cpp b/packages/nodegraph/source/common/core/NodeGraphInput.cpp index 15fc0ce7..9bf778d6 100644 --- a/packages/nodegraph/source/common/core/NodeGraphInput.cpp +++ b/packages/nodegraph/source/common/core/NodeGraphInput.cpp @@ -25,6 +25,8 @@ namespace l::nodegraph { return { 0.0f, 0.0f }; } + /*********************************************************************************/ + void NodeGraphInput::Reset() { if (mInputType == InputType::INPUT_NODE || mInputType == InputType::INPUT_VALUE) { mInput.mInputNode = nullptr; @@ -102,4 +104,178 @@ namespace l::nodegraph { } return 1; } + + /*********************************************************************************/ + + void NodeGraphInputAccessor::SetDuration(int32_t ticks) { + switch (mType) { + case InputTypeBase::INTERP_RWA: + mInput.mFilterRWA.SetConvergenceInTicks(static_cast(ticks)); + break; + case InputTypeBase::INTERP_RWA_MS: + case InputTypeBase::CUSTOM_VALUE_INTERP_RWA_MS: + mInput.mFilterRWA.SetConvergenceInTicks(static_cast(ticks)); + break; + case InputTypeBase::INTERP_TWEEN: + case InputTypeBase::INTERP_TWEEN_MS: + mInput.mTween.SetTweenLength(ticks); + break; + case InputTypeBase::SAMPLED: + case InputTypeBase::CONSTANT_VALUE: + case InputTypeBase::CONSTANT_ARRAY: + ASSERT(false) << "Failed to set convergence on a non interpolated input type"; + break; + } + } + + void NodeGraphInputAccessor::SetDuration(float ms) { + switch (mType) { + case InputTypeBase::INTERP_RWA: + mInput.mFilterRWA.SetConvergenceInMs(ms); + break; + case InputTypeBase::INTERP_RWA_MS: + case InputTypeBase::CUSTOM_VALUE_INTERP_RWA_MS: + mInput.mFilterRWA.SetConvergenceInMs(ms); + break; + case InputTypeBase::INTERP_TWEEN: + case InputTypeBase::INTERP_TWEEN_MS: + mInput.mTween.SetTweenLength(l::audio::GetAudioTicksFromMS(ms)); + break; + case InputTypeBase::SAMPLED: + case InputTypeBase::CONSTANT_VALUE: + case InputTypeBase::CONSTANT_ARRAY: + ASSERT(false) << "Failed to set convergence on a non interpolated input type"; + break; + } + } + + void NodeGraphInputAccessor::SetTarget(float value) { + switch (mType) { + case InputTypeBase::INTERP_RWA: + case InputTypeBase::INTERP_RWA_MS: + case InputTypeBase::CUSTOM_VALUE_INTERP_RWA_MS: + mInput.mFilterRWA.SetTarget(value); + break; + case InputTypeBase::INTERP_TWEEN: + case InputTypeBase::INTERP_TWEEN_MS: + mInput.mTween.SetTarget(value); + break; + case InputTypeBase::SAMPLED: + case InputTypeBase::CONSTANT_VALUE: + case InputTypeBase::CONSTANT_ARRAY: + break; + } + } + + void NodeGraphInputAccessor::SetValue(float value) { + switch (mType) { + case InputTypeBase::INTERP_RWA: + case InputTypeBase::INTERP_RWA_MS: + case InputTypeBase::CUSTOM_VALUE_INTERP_RWA_MS: + mInput.mFilterRWA.Value() = value; + break; + case InputTypeBase::INTERP_TWEEN: + case InputTypeBase::INTERP_TWEEN_MS: + mInput.mTween.Reset(value, true); + break; + case InputTypeBase::SAMPLED: + case InputTypeBase::CONSTANT_VALUE: + case InputTypeBase::CONSTANT_ARRAY: + break; + } + } + + float NodeGraphInputAccessor::GetValueNext() { + switch (mType) { + case InputTypeBase::INTERP_RWA: + case InputTypeBase::INTERP_RWA_MS: + case InputTypeBase::CUSTOM_VALUE_INTERP_RWA_MS: + return mInput.mFilterRWA.Next(); + case InputTypeBase::INTERP_TWEEN: + case InputTypeBase::INTERP_TWEEN_MS: + return mInput.mTween.Next(); + case InputTypeBase::SAMPLED: + case InputTypeBase::CONSTANT_ARRAY: + return *mInput.mIterator++; + case InputTypeBase::CONSTANT_VALUE: + return *mInput.mIterator; + } + return 0.0f; + } + + float NodeGraphInputAccessor::GetValue() { + switch (mType) { + case InputTypeBase::INTERP_RWA: + case InputTypeBase::INTERP_RWA_MS: + case InputTypeBase::CUSTOM_VALUE_INTERP_RWA_MS: + return mInput.mFilterRWA.Value(); + case InputTypeBase::INTERP_TWEEN: + case InputTypeBase::INTERP_TWEEN_MS: + return mInput.mTween.Value(); + case InputTypeBase::SAMPLED: + case InputTypeBase::CONSTANT_VALUE: + case InputTypeBase::CONSTANT_ARRAY: + return *mInput.mIterator; + } + return 0.0f; + } + + float NodeGraphInputAccessor::GetArrayValue(int32_t index) { + if (mType == InputTypeBase::CONSTANT_ARRAY) { + return mInput.mIterator[index]; + } + return 0.0f; + } + + float* NodeGraphInputAccessor::GetArray() { + if (mType == InputTypeBase::CONSTANT_ARRAY) { + return mInput.mIterator.data(); + } + return nullptr; + } + + // run on each new batch call to setup input iterators for buffered data + void NodeGraphInputAccessor::ProcessUpdate(std::vector& input, int32_t numSamples, float updateRate) { + switch (mType) { + case InputTypeBase::INTERP_RWA: + case InputTypeBase::INTERP_RWA_MS: + case InputTypeBase::CUSTOM_VALUE_INTERP_RWA_MS: + mInput.mFilterRWA.SetRWAUpdateRate(updateRate); + break; + case InputTypeBase::INTERP_TWEEN: + case InputTypeBase::INTERP_TWEEN_MS: + break; + case InputTypeBase::SAMPLED: + mInput.mIterator = input.at(mInputIndex).GetBufferIterator(numSamples); + break; + case InputTypeBase::CONSTANT_VALUE: + mInput.mIterator.Reset(&input.at(mInputIndex).Get(), 1); + break; + case InputTypeBase::CONSTANT_ARRAY: + mInput.mIterator = input.at(mInputIndex).GetArrayIterator(); + break; + } + } + + // run on each node update (can be node specific) and will update node rwa filters + void NodeGraphInputAccessor::NodeUpdate(std::vector& input, float updateRate) { + switch (mType) { + case InputTypeBase::INTERP_RWA: + case InputTypeBase::INTERP_RWA_MS: + mInput.mFilterRWA.SetRWAUpdateRate(updateRate); + mInput.mFilterRWA.SetTarget(input.at(mInputIndex).Get()); + break; + case InputTypeBase::INTERP_TWEEN: + case InputTypeBase::INTERP_TWEEN_MS: + mInput.mTween.Update(); + break; + case InputTypeBase::CUSTOM_VALUE_INTERP_RWA_MS: // must set it manually + mInput.mFilterRWA.SetRWAUpdateRate(updateRate); + break; + case InputTypeBase::SAMPLED: + case InputTypeBase::CONSTANT_VALUE: + case InputTypeBase::CONSTANT_ARRAY: + break; + } + } } \ No newline at end of file diff --git a/packages/nodegraph/source/common/operations/NodeGraphOpControl.cpp b/packages/nodegraph/source/common/operations/NodeGraphOpControl.cpp index f9c96b94..7058fa1d 100644 --- a/packages/nodegraph/source/common/operations/NodeGraphOpControl.cpp +++ b/packages/nodegraph/source/common/operations/NodeGraphOpControl.cpp @@ -12,23 +12,26 @@ namespace l::nodegraph { /*********************************************************************/ void GraphControlBase::Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) { + mNodeInputManager.ProcessUpdate(inputs, numSamples, mUpdateRate); + float sync = inputs.at(0).Get(); + mUpdateRate = mNodeInputManager.GetValue(1); if (sync > 0.5f) { mSamplesUntilUpdate = 0; - mUpdateRate = mNodeInputManager.GetValue(1); } auto output0 = outputs.at(0).GetIterator(numSamples); auto output1 = outputs.at(1).GetIterator(numSamples); - mNodeInputManager.ProcessUpdate(inputs, numSamples); mSamplesUntilUpdate = l::audio::BatchUpdate(mUpdateRate, mSamplesUntilUpdate, 0, numSamples, [&]() { - mNodeInputManager.NodeUpdate(inputs); + mNodeInputManager.NodeUpdate(inputs, mUpdateRate); mUpdateRate = mNodeInputManager.GetValue(1); UpdateSignal(mNodeInputManager); + + return mUpdateRate; }, [&](int32_t start, int32_t end, bool) { for (int32_t i = start; i < end; i++) { @@ -46,9 +49,9 @@ namespace l::nodegraph { mFreqTarget = inputManager.GetValueNext(mFreqTargetId); inputManager.SetTarget(mFreqId, mFreqTarget); - mAttackFrames = l::audio::GetSamplesFromMS(inputManager.GetValue(4)); - mReleaseFrames = l::audio::GetSamplesFromMS(inputManager.GetValue(5)); - mAttackFactor = l::audio::GetRWAFactorFromMSAttackSkew(inputManager.GetValue(4), 0.0001f); + mAttackFrames = l::audio::GetAudioTicksFromMS(inputManager.GetValue(4)); + mReleaseFrames = l::audio::GetAudioTicksFromMS(inputManager.GetValue(5)); + mAttackFactor = l::audio::GetRWAFactorFromMSSkewed(inputManager.GetValue(4), 0.0001f); mReleaseFactor = l::audio::GetRWAFactorFromMS(inputManager.GetValue(5), 0.0001f); } @@ -139,13 +142,10 @@ namespace l::nodegraph { } void GraphControlArpeggio::Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) { - mNodeInputManager.ProcessUpdate(inputs, numSamples); - + mNodeInputManager.ProcessUpdate(inputs, numSamples, mUpdateRate); float sync = mNodeInputManager.GetValue(0); - float bpm = mNodeInputManager.GetValueNext(1); - + float bpm = mNodeInputManager.GetValue(1); mUpdateRate = 44100.0f * 60.0f / (4.0f * bpm); - if (sync > 0.5f) { mSamplesUntilUpdate = 0; } @@ -188,10 +188,12 @@ namespace l::nodegraph { auto output1 = outputs.at(1).GetIterator(numSamples); mSamplesUntilUpdate = l::audio::BatchUpdate(mUpdateRate, mSamplesUntilUpdate, 0, numSamples, [&]() { - mNodeInputManager.NodeUpdate(inputs); + mNodeInputManager.NodeUpdate(inputs, mUpdateRate); mUpdateRate = mNodeInputManager.GetValue(1); UpdateSignal(mNodeInputManager); + + return mUpdateRate; }, [&](int32_t start, int32_t end, bool) { for (int32_t i = start; i < end; i++) { diff --git a/packages/nodegraph/source/common/operations/NodeGraphOpEffect.cpp b/packages/nodegraph/source/common/operations/NodeGraphOpEffect.cpp index 087cfcac..78679507 100644 --- a/packages/nodegraph/source/common/operations/NodeGraphOpEffect.cpp +++ b/packages/nodegraph/source/common/operations/NodeGraphOpEffect.cpp @@ -22,8 +22,8 @@ namespace l::nodegraph { auto output0 = outputs.at(0).GetIterator(numSamples); auto output1 = outputs.at(1).GetIterator(numSamples); - mFilterGain.SetConvergence(); - mFilterMix.SetConvergence(); + mFilterGain.SetConvergenceFactor(); + mFilterMix.SetConvergenceFactor(); mSamplesUntilUpdate = l::audio::BatchUpdate(mUpdateRate, mSamplesUntilUpdate, 0, numSamples, [&]() { @@ -32,6 +32,8 @@ namespace l::nodegraph { mFilterMix.SetTarget(inputs.at(3).Get()); UpdateSignal(inputs, outputs); + + return mUpdateRate; }, [&](int32_t start, int32_t end, bool) { for (int32_t i = start; i < end; i++) { @@ -363,13 +365,15 @@ namespace l::nodegraph { mGateSmoothing = attack * attack; mGateSmoothingNeg = mGateSmoothing; - float freq = 44100.0f * 60.0f / bpm; + float updateRate = 44100.0f * 60.0f / bpm; - mSamplesUntilUpdate = l::audio::BatchUpdate(freq * fmodPerPattern, mSamplesUntilUpdate, 0, numSamples, + mSamplesUntilUpdate = l::audio::BatchUpdate(updateRate * fmodPerPattern, mSamplesUntilUpdate, 0, numSamples, [&]() { mGateIndex %= gate.size(); mGainTarget = gate[mGateIndex]; mGateIndex++; + + return updateRate * fmodPerPattern; }, [&](int32_t start, int32_t end, bool) { for (int32_t i = start; i < end; i++) { diff --git a/packages/nodegraph/source/common/operations/NodeGraphOpFilter.cpp b/packages/nodegraph/source/common/operations/NodeGraphOpFilter.cpp index e5451e90..220e1317 100644 --- a/packages/nodegraph/source/common/operations/NodeGraphOpFilter.cpp +++ b/packages/nodegraph/source/common/operations/NodeGraphOpFilter.cpp @@ -34,7 +34,7 @@ namespace l::nodegraph { auto input = &inputs.at(1).Get(numSamples); auto output = &outputs.at(0).Get(numSamples); - mSamplesUntilUpdate = l::audio::BatchUpdate(mUpdateSamples, mSamplesUntilUpdate, 0, numSamples, + mSamplesUntilUpdate = l::audio::BatchUpdate(mUpdateRate, mSamplesUntilUpdate, 0, numSamples, [&]() { mReset = inputs.at(0).Get(); if (mReset > 0.5f) { @@ -45,10 +45,12 @@ namespace l::nodegraph { mCutoffCPS = l::math::functions::sin(l::math::constants::PI_f * mCutoff * mCutoff * 0.5f); mResonance = 1.0f - inputs.at(3).Get(); - mCutoffFilter.SetConvergence().SetTarget(mCutoffCPS).SnapAt(); - mResonanceFilter.SetConvergence().SetTarget(mResonance).SnapAt(); + mCutoffFilter.SetConvergenceFactor().SetTarget(mCutoffCPS).SnapAt(); + mResonanceFilter.SetConvergenceFactor().SetTarget(mResonance).SnapAt(); UpdateSignal(inputs, outputs); + + return mUpdateRate; }, [&](int32_t start, int32_t end, bool) { for (int32_t i = start; i < end; i++) { @@ -113,7 +115,7 @@ namespace l::nodegraph { void GraphFilterChamberlain2pole::UpdateSignal(std::vector& inputs, std::vector&) { mMode = static_cast(3.0f * inputs.at(mNumDefaultInputs + 0).Get() + 0.5f); mScale = l::math::functions::sqrt(mResonance); - mScaleFilter.SetConvergence().SetTarget(mScale).SnapAt(); + mScaleFilter.SetConvergenceFactor().SetTarget(mScale).SnapAt(); } float GraphFilterChamberlain2pole::ProcessSignal(float input, float cutoff, float resonance) { diff --git a/packages/nodegraph/source/common/operations/NodeGraphOpOutput.cpp b/packages/nodegraph/source/common/operations/NodeGraphOpOutput.cpp index d2037ad7..5fc9ecad 100644 --- a/packages/nodegraph/source/common/operations/NodeGraphOpOutput.cpp +++ b/packages/nodegraph/source/common/operations/NodeGraphOpOutput.cpp @@ -27,7 +27,8 @@ namespace l::nodegraph { auto in0 = inputs.at(0).GetBufferIterator(numSamples); auto in1 = inputs.at(1).GetBufferIterator(numSamples); - mSamplesUntilUpdate = l::audio::BatchUpdate(256.0f, mSamplesUntilUpdate, 0, numSamples, + float updateRate = 256.0f; + mSamplesUntilUpdate = l::audio::BatchUpdate(updateRate, mSamplesUntilUpdate, 0, numSamples, [&]() { auto volume = inputs.at(2).Get(); mFilterEnvelope.SetTarget(volume * volume).SnapAt(); @@ -38,6 +39,8 @@ namespace l::nodegraph { mRelease = l::math::functions::pow(0.001f, 1.0f / (releaseMs * 44100.0f * 0.001f)); mAttack = 0.01f; mRelease = 0.01f; + + return updateRate; }, [&](int32_t start, int32_t end, bool) { @@ -84,7 +87,7 @@ namespace l::nodegraph { /*********************************************************************/ void GraphOutputPlot::Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) { - mNodeInputManager.ProcessUpdate(inputs, numSamples); + mNodeInputManager.ProcessUpdate(inputs, numSamples, 1.0f); int32_t outputSize = outputs.at(0).GetSize(); float* output = &outputs.at(0).Get(outputSize); diff --git a/packages/nodegraph/source/common/operations/NodeGraphOpSignal.cpp b/packages/nodegraph/source/common/operations/NodeGraphOpSignal.cpp index bff8a405..9b5a895b 100644 --- a/packages/nodegraph/source/common/operations/NodeGraphOpSignal.cpp +++ b/packages/nodegraph/source/common/operations/NodeGraphOpSignal.cpp @@ -11,6 +11,8 @@ namespace l::nodegraph { /*********************************************************************/ void GraphSignalBase::Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) { + mNodeInputManager.ProcessUpdate(inputs, numSamples, mUpdateRate); + float sync = inputs.at(0).Get(); if (sync > 0.5f) { mSamplesUntilUpdate = 0; @@ -18,9 +20,12 @@ namespace l::nodegraph { float* output0 = &outputs.at(0).Get(numSamples); - mSamplesUntilUpdate = l::audio::BatchUpdate(mUpdateSamples, mSamplesUntilUpdate, 0, numSamples, + mSamplesUntilUpdate = l::audio::BatchUpdate(mUpdateRate, mSamplesUntilUpdate, 0, numSamples, [&]() { - mUpdateSamples = inputs.at(1).Get(); + mUpdateRate = inputs.at(1).Get(); + mNodeInputManager.NodeUpdate(inputs, mUpdateRate); + + mFreq = inputs.at(2).Get(); if (mFreq <= 0.0f) { @@ -32,15 +37,17 @@ namespace l::nodegraph { } mSmooth = inputs.at(4).Get(); - if (mFilterVolume.SetConvergence().SetTarget(mVolumeTarget).SnapAt()) { + if (mFilterVolume.SetConvergenceFactor().SetTarget(mVolumeTarget).SnapAt()) { mVolumeTarget = 0.0f; ResetSignal(); } - mFilterSignal.SetConvergence(mSmooth); + mFilterSignal.SetConvergenceFactor(mSmooth); mDeltaTime = 1.0f / 44100.0f; UpdateSignal(inputs, outputs); + + return mUpdateRate; }, [&](int32_t start, int32_t end, bool) { for (int32_t i = start; i < end; i++) { @@ -58,7 +65,7 @@ namespace l::nodegraph { mNode->SetInput(mNumDefaultInputs + 1, 0.0f); mNode->SetInputBound(mNumDefaultInputs + 0, InputBound::INPUT_0_TO_1); mNode->SetInputBound(mNumDefaultInputs + 1, InputBound::INPUT_0_TO_1); - mUpdateSamples = 16.0f; + mUpdateRate = 16.0f; } void GraphSignalSine2::ResetSignal() { @@ -71,8 +78,8 @@ namespace l::nodegraph { mPmod = inputs.at(mNumDefaultInputs + 1).Get(); mFmod *= 0.25f * 0.25f * 0.5f * 44100.0f / l::math::functions::max(mFreq, 1.0f); - mFilterFmod.SetConvergence().SetTarget(mFmod); - mFilterPmod.SetConvergence().SetTarget(mPmod); + mFilterFmod.SetConvergenceFactor().SetTarget(mFmod); + mFilterPmod.SetConvergenceFactor().SetTarget(mPmod); } float GraphSignalSine2::ProcessSignal(float deltaTime, float freq) { @@ -100,7 +107,7 @@ namespace l::nodegraph { mNode->SetInput(mNumDefaultInputs + 1, 0.0f); mNode->SetInputBound(mNumDefaultInputs + 0, InputBound::INPUT_0_TO_1); mNode->SetInputBound(mNumDefaultInputs + 1, InputBound::INPUT_0_TO_1); - mUpdateSamples = 16.0f; + mUpdateRate = 16.0f; } void GraphSignalSaw2::ResetSignal() { @@ -122,7 +129,9 @@ namespace l::nodegraph { void GraphSignalSine::Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) { float* output0 = &outputs.at(0).Get(numSamples); - mSamplesUntilUpdate = l::audio::BatchUpdate(256.0f, mSamplesUntilUpdate, 0, numSamples, + float updateRate = 256.0f; + + mSamplesUntilUpdate = l::audio::BatchUpdate(updateRate, mSamplesUntilUpdate, 0, numSamples, [&]() { mFreq = l::math::functions::max(static_cast(inputs.at(0).Get()), 0.0); mVolume = inputs.at(1).Get(); @@ -131,13 +140,14 @@ namespace l::nodegraph { if (mFreq == 0.0f) { mVolume = 0.0f; outputs.at(0).mOutput = 0.0f; - return; + return updateRate; } if (mReset > 0.5f) { mVolume = 0.0f; } mDeltaTime = 1.0 / 44100.0; + return updateRate; }, [&](int32_t start, int32_t end, bool) { @@ -193,7 +203,8 @@ namespace l::nodegraph { void GraphSignalSineFM::Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) { float* output0 = &outputs.at(0).Get(numSamples); - mSamplesUntilUpdate = l::audio::BatchUpdate(256.0f, mSamplesUntilUpdate, 0, numSamples, + float updateRate = 256.0f; + mSamplesUntilUpdate = l::audio::BatchUpdate(updateRate, mSamplesUntilUpdate, 0, numSamples, [&]() { mFreq = l::math::functions::max(static_cast(inputs.at(0).Get()), 0.0); mVolume = inputs.at(1).Get(); @@ -204,10 +215,11 @@ namespace l::nodegraph { mPhaseFmod = 0.0; mVolume = 0.0f; outputs.at(0).mOutput = 0.0f; - return; + return updateRate; } mDeltaTime = 1.0 / 44100.0; + return updateRate; }, [&](int32_t start, int32_t end, bool) { @@ -246,7 +258,8 @@ namespace l::nodegraph { void GraphSignalSineFM2::Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) { float* output0 = &outputs.at(0).Get(numSamples); - mSamplesUntilUpdate = l::audio::BatchUpdate(256.0f, mSamplesUntilUpdate, 0, numSamples, + float updateRate = 256.0f; + mSamplesUntilUpdate = l::audio::BatchUpdate(updateRate, mSamplesUntilUpdate, 0, numSamples, [&]() { mFreq = l::math::functions::max(static_cast(inputs.at(0).Get()), 0.0); mVolume = inputs.at(1).Get(); @@ -257,11 +270,12 @@ namespace l::nodegraph { mPhaseFmod = 0.0; mVolume = 0.0f; outputs.at(0).mOutput = 0.0f; - return; + return updateRate; } mDeltaTime = 1.0 / 44100.0; mDeltaLimit = mDeltaTime * 2.0; + return updateRate; }, [&](int32_t start, int32_t end, bool) { @@ -304,7 +318,8 @@ namespace l::nodegraph { void GraphSignalSineFM3::Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) { float* output0 = &outputs.at(0).Get(numSamples); - mSamplesUntilUpdate = l::audio::BatchUpdate(16.0f, mSamplesUntilUpdate, 0, numSamples, + float updateRate = 256.0f; + mSamplesUntilUpdate = l::audio::BatchUpdate(updateRate, mSamplesUntilUpdate, 0, numSamples, [&]() { mFreq = l::math::functions::max(static_cast(inputs.at(0).Get()), 0.0); mVolume = inputs.at(1).Get(); @@ -315,10 +330,12 @@ namespace l::nodegraph { mPhaseFmod = 0.0; mVolume = 0.0f; outputs.at(0).mOutput = 0.0f; - return; + return updateRate; } mDeltaTime = 1.0 / 44100.0; mDeltaLimit = mDeltaTime * 4.0; + + return updateRate; }, [&](int32_t start, int32_t end, bool) { @@ -352,7 +369,8 @@ namespace l::nodegraph { void GraphSignalSaw::Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) { float* output0 = &outputs.at(0).Get(numSamples); - mSamplesUntilUpdate = l::audio::BatchUpdate(256.0f, mSamplesUntilUpdate, 0, numSamples, + float updateRate = 256.0f; + mSamplesUntilUpdate = l::audio::BatchUpdate(updateRate, mSamplesUntilUpdate, 0, numSamples, [&]() { mFreq = l::math::functions::max(static_cast(inputs.at(0).Get()), 0.0); mVolume = inputs.at(1).Get(); @@ -361,13 +379,14 @@ namespace l::nodegraph { if (mFreq == 0.0f) { mVolume = 0.0f; outputs.at(0).mOutput = 0.0f; - return; + return updateRate; } if (mReset > 0.5f) { mVolume = 0.0f; } mDeltaTime = 1.0 / 44100.0; + return updateRate; }, [&](int32_t start, int32_t end, bool) { From 76ae7950d588f0e6c0febc1ef6eb33557c7979e5 Mon Sep 17 00:00:00 2001 From: lnd3 Date: Wed, 18 Sep 2024 02:50:44 +0200 Subject: [PATCH 25/42] Fix order. --- .../include/nodegraph/operations/NodeGraphOpOutput.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpOutput.h b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpOutput.h index 20f98131..cd1807d9 100644 --- a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpOutput.h +++ b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpOutput.h @@ -75,8 +75,8 @@ namespace l::nodegraph { public: GraphOutputPlot(NodeGraphBase* node, int32_t plotSamples) : NodeGraphOp(node, "Plot"), - mPlotSamples(plotSamples), - mNodeInputManager(*this) + mNodeInputManager(*this), + mPlotSamples(plotSamples) { mNodeInputManager.AddInputBase(InputTypeBase::SAMPLED, AddInput("Plot", 0.0f, 1, -1.0f, 1.0f)); AddOutput("Plot", 0.0f, mPlotSamples); From e5c76dfee1bb367785e378971445b4beb5cbc9d9 Mon Sep 17 00:00:00 2001 From: lnd3 Date: Wed, 18 Sep 2024 08:48:21 +0200 Subject: [PATCH 26/42] Make filters use input manager. Fix resonance error. Make tweens custom only since they require a custom duration and aren't suitable for continous smoothing. They should be used for keyed events like envelopes. --- .../include/nodegraph/core/NodeGraphInput.h | 12 ++--- .../nodegraph/operations/NodeGraphOpControl.h | 6 +-- .../nodegraph/operations/NodeGraphOpFilter.h | 24 +++++----- .../nodegraph/operations/NodeGraphOpSignal.h | 6 +-- .../source/common/core/NodeGraphBase.cpp | 4 +- .../source/common/core/NodeGraphInput.cpp | 48 +++++++++---------- .../common/operations/NodeGraphOpFilter.cpp | 36 ++++++-------- 7 files changed, 63 insertions(+), 73 deletions(-) diff --git a/packages/nodegraph/include/nodegraph/core/NodeGraphInput.h b/packages/nodegraph/include/nodegraph/core/NodeGraphInput.h index 8ce92738..512a7a50 100644 --- a/packages/nodegraph/include/nodegraph/core/NodeGraphInput.h +++ b/packages/nodegraph/include/nodegraph/core/NodeGraphInput.h @@ -108,11 +108,11 @@ namespace l::nodegraph { SAMPLED = 0, INTERP_RWA, INTERP_RWA_MS, - INTERP_TWEEN, - INTERP_TWEEN_MS, CONSTANT_VALUE, CONSTANT_ARRAY, - CUSTOM_VALUE_INTERP_RWA_MS + CUSTOM_INTERP_TWEEN, + CUSTOM_INTERP_TWEEN_MS, + CUSTOM_INTERP_RWA_MS }; union InputUnion { @@ -135,14 +135,14 @@ namespace l::nodegraph { switch(mType) { case InputTypeBase::INTERP_RWA: case InputTypeBase::INTERP_RWA_MS: - case InputTypeBase::CUSTOM_VALUE_INTERP_RWA_MS: + case InputTypeBase::CUSTOM_INTERP_RWA_MS: //new (&s.vec) std::vector; new (&mInput.mFilterRWA) l::audio::FilterRWA(); //mInput.mFilter = l::audio::FilterRWA(); break; - case InputTypeBase::INTERP_TWEEN: - case InputTypeBase::INTERP_TWEEN_MS: + case InputTypeBase::CUSTOM_INTERP_TWEEN: + case InputTypeBase::CUSTOM_INTERP_TWEEN_MS: new (&mInput.mTween) l::math::tween::DynamicTween(); break; case InputTypeBase::SAMPLED: diff --git a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpControl.h b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpControl.h index b1d6df1d..5e560c18 100644 --- a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpControl.h +++ b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpControl.h @@ -34,8 +34,8 @@ namespace l::nodegraph { { mNodeInputManager.AddInputBase(InputTypeBase::CONSTANT_VALUE, AddInput("Sync", 0.0f, 1, 0.0f, 1.0f)); mNodeInputManager.AddInputBase(InputTypeBase::CONSTANT_VALUE, AddInput("Rate", 256.0f, 1, 1.0f, 2048.0f)); - mNodeInputManager.AddInputBase(InputTypeBase::INTERP_TWEEN, AddInput("Velocity", 0.5f, 1, 0.0f, 1.0f)); - mNodeInputManager.AddInputBase(InputTypeBase::INTERP_TWEEN, AddInput("Fade", 0.1f, 1, 0.0001f, 1.0f)); + mNodeInputManager.AddInputBase(InputTypeBase::CUSTOM_INTERP_TWEEN, AddInput("Velocity", 0.5f, 1, 0.0f, 1.0f)); + mNodeInputManager.AddInputBase(InputTypeBase::CUSTOM_INTERP_TWEEN, AddInput("Fade", 0.1f, 1, 0.0001f, 1.0f)); mNodeInputManager.AddInputBase(InputTypeBase::CONSTANT_VALUE, AddInput("Attack", 50.0f, 1, 1.0f, 10000.0f)); mNodeInputManager.AddInputBase(InputTypeBase::CONSTANT_VALUE, AddInput("Release", 50.0f, 1, 1.0f, 10000.0f)); @@ -65,7 +65,7 @@ namespace l::nodegraph { GraphControlBase(node, "Envelope") { mFreqTargetId = mNodeInputManager.AddInputBase(InputTypeBase::CONSTANT_VALUE, AddInput("Freq")); - mFreqId = mNodeInputManager.AddInputBase(InputTypeBase::CUSTOM_VALUE_INTERP_RWA_MS); + mFreqId = mNodeInputManager.AddInputBase(InputTypeBase::CUSTOM_INTERP_RWA_MS); mEnvelope = 0.0f; } diff --git a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpFilter.h b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpFilter.h index 2b1277e4..39a78a0d 100644 --- a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpFilter.h +++ b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpFilter.h @@ -30,16 +30,18 @@ namespace l::nodegraph { static const int8_t mNumDefaultOutputs = 1; GraphFilterBase(NodeGraphBase* node, std::string_view name) : - NodeGraphOp(node, name) + NodeGraphOp(node, name), + mNodeInputManager(*this) { mDefaultInStrings.clear(); mDefaultOutStrings.clear(); mDefaultInData.clear(); - AddInput("Sync", 0.0f); - AddInput("In"); - AddInput("Cutoff", 1.0f); - AddInput("Resonance", 0.0f); + mNodeInputManager.AddInputBase(InputTypeBase::CONSTANT_VALUE, AddInput("Sync", 0.0f)); + mNodeInputManager.AddInputBase(InputTypeBase::SAMPLED, AddInput("In")); + mNodeInputManager.AddInputBase(InputTypeBase::INTERP_RWA, AddInput("Cutoff", 1.0f)); + mNodeInputManager.AddInputBase(InputTypeBase::INTERP_RWA, AddInput("Resonance", 0.0f)); + AddOutput("Out", 0.0f); } @@ -52,15 +54,11 @@ namespace l::nodegraph { virtual void UpdateSignal(std::vector&, std::vector&) {}; virtual float ProcessSignal(float input, float cutoff, float resonance) = 0; protected: - float mReset = 0.0f; - float mSamplesUntilUpdate = 0.0f; - float mUpdateRate = 16.0f; + NodeInputManager mNodeInputManager; - float mCutoff = 0.0f; - float mResonance = 0.0f; - float mCutoffCPS = 0.0f; // cutoff cycles per sample - l::audio::FilterRWA mCutoffFilter; - l::audio::FilterRWA mResonanceFilter; + float mSync = 0.0f; + float mSamplesUntilUpdate = 0.0f; + float mUpdateRate = 128.0f; }; /*********************************************************************/ diff --git a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpSignal.h b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpSignal.h index 1e5c17d9..02d57a01 100644 --- a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpSignal.h +++ b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpSignal.h @@ -34,9 +34,9 @@ namespace l::nodegraph { { mNodeInputManager.AddInputBase(InputTypeBase::CONSTANT_VALUE, AddInput("Sync", 0.0f, 1, 0.0f, 1.0f)); mNodeInputManager.AddInputBase(InputTypeBase::CONSTANT_VALUE, AddInput("Rate", 256.0f, 1, 1.0f, 2048.0f)); - mNodeInputManager.AddInputBase(InputTypeBase::INTERP_TWEEN, AddInput("Freq", 0.0f, 1, 0.0f, 22050.0f)); - mNodeInputManager.AddInputBase(InputTypeBase::INTERP_TWEEN, AddInput("Volume", 0.5f, 1, 0.0f, 5.0f)); - mNodeInputManager.AddInputBase(InputTypeBase::INTERP_TWEEN, AddInput("Smooth", 1.0f, 1, 0.0f, 1.0f)); + mNodeInputManager.AddInputBase(InputTypeBase::CUSTOM_INTERP_TWEEN, AddInput("Freq", 0.0f, 1, 0.0f, 22050.0f)); + mNodeInputManager.AddInputBase(InputTypeBase::CUSTOM_INTERP_TWEEN, AddInput("Volume", 0.5f, 1, 0.0f, 5.0f)); + mNodeInputManager.AddInputBase(InputTypeBase::CUSTOM_INTERP_TWEEN, AddInput("Smooth", 1.0f, 1, 0.0f, 1.0f)); AddOutput("Out", 0.0f, 2); diff --git a/packages/nodegraph/source/common/core/NodeGraphBase.cpp b/packages/nodegraph/source/common/core/NodeGraphBase.cpp index 66edb6f6..9df32d67 100644 --- a/packages/nodegraph/source/common/core/NodeGraphBase.cpp +++ b/packages/nodegraph/source/common/core/NodeGraphBase.cpp @@ -373,14 +373,14 @@ namespace l::nodegraph { /**********************************************************************************/ int32_t NodeInputManager::AddInputBase(InputTypeBase type, int32_t inputIndex) { - if (type != InputTypeBase::CUSTOM_VALUE_INTERP_RWA_MS) { + if (type != InputTypeBase::CUSTOM_INTERP_RWA_MS) { ASSERT(inputIndex >= 0); } inputIndex = static_cast(mInputs.size()); mInputs.emplace_back(NodeGraphInputAccessor{ type, inputIndex }); - if (type != InputTypeBase::CUSTOM_VALUE_INTERP_RWA_MS) { + if (type != InputTypeBase::CUSTOM_INTERP_RWA_MS) { mInputs.back().SetValue(mNodeGraphOperation.GetDefaultData(static_cast(inputIndex))); mInputs.back().SetTarget(mNodeGraphOperation.GetDefaultData(static_cast(inputIndex))); } diff --git a/packages/nodegraph/source/common/core/NodeGraphInput.cpp b/packages/nodegraph/source/common/core/NodeGraphInput.cpp index 9bf778d6..5470eb32 100644 --- a/packages/nodegraph/source/common/core/NodeGraphInput.cpp +++ b/packages/nodegraph/source/common/core/NodeGraphInput.cpp @@ -113,11 +113,11 @@ namespace l::nodegraph { mInput.mFilterRWA.SetConvergenceInTicks(static_cast(ticks)); break; case InputTypeBase::INTERP_RWA_MS: - case InputTypeBase::CUSTOM_VALUE_INTERP_RWA_MS: + case InputTypeBase::CUSTOM_INTERP_RWA_MS: mInput.mFilterRWA.SetConvergenceInTicks(static_cast(ticks)); break; - case InputTypeBase::INTERP_TWEEN: - case InputTypeBase::INTERP_TWEEN_MS: + case InputTypeBase::CUSTOM_INTERP_TWEEN: + case InputTypeBase::CUSTOM_INTERP_TWEEN_MS: mInput.mTween.SetTweenLength(ticks); break; case InputTypeBase::SAMPLED: @@ -134,11 +134,11 @@ namespace l::nodegraph { mInput.mFilterRWA.SetConvergenceInMs(ms); break; case InputTypeBase::INTERP_RWA_MS: - case InputTypeBase::CUSTOM_VALUE_INTERP_RWA_MS: + case InputTypeBase::CUSTOM_INTERP_RWA_MS: mInput.mFilterRWA.SetConvergenceInMs(ms); break; - case InputTypeBase::INTERP_TWEEN: - case InputTypeBase::INTERP_TWEEN_MS: + case InputTypeBase::CUSTOM_INTERP_TWEEN: + case InputTypeBase::CUSTOM_INTERP_TWEEN_MS: mInput.mTween.SetTweenLength(l::audio::GetAudioTicksFromMS(ms)); break; case InputTypeBase::SAMPLED: @@ -153,11 +153,11 @@ namespace l::nodegraph { switch (mType) { case InputTypeBase::INTERP_RWA: case InputTypeBase::INTERP_RWA_MS: - case InputTypeBase::CUSTOM_VALUE_INTERP_RWA_MS: + case InputTypeBase::CUSTOM_INTERP_RWA_MS: mInput.mFilterRWA.SetTarget(value); break; - case InputTypeBase::INTERP_TWEEN: - case InputTypeBase::INTERP_TWEEN_MS: + case InputTypeBase::CUSTOM_INTERP_TWEEN: + case InputTypeBase::CUSTOM_INTERP_TWEEN_MS: mInput.mTween.SetTarget(value); break; case InputTypeBase::SAMPLED: @@ -171,11 +171,11 @@ namespace l::nodegraph { switch (mType) { case InputTypeBase::INTERP_RWA: case InputTypeBase::INTERP_RWA_MS: - case InputTypeBase::CUSTOM_VALUE_INTERP_RWA_MS: + case InputTypeBase::CUSTOM_INTERP_RWA_MS: mInput.mFilterRWA.Value() = value; break; - case InputTypeBase::INTERP_TWEEN: - case InputTypeBase::INTERP_TWEEN_MS: + case InputTypeBase::CUSTOM_INTERP_TWEEN: + case InputTypeBase::CUSTOM_INTERP_TWEEN_MS: mInput.mTween.Reset(value, true); break; case InputTypeBase::SAMPLED: @@ -189,10 +189,10 @@ namespace l::nodegraph { switch (mType) { case InputTypeBase::INTERP_RWA: case InputTypeBase::INTERP_RWA_MS: - case InputTypeBase::CUSTOM_VALUE_INTERP_RWA_MS: + case InputTypeBase::CUSTOM_INTERP_RWA_MS: return mInput.mFilterRWA.Next(); - case InputTypeBase::INTERP_TWEEN: - case InputTypeBase::INTERP_TWEEN_MS: + case InputTypeBase::CUSTOM_INTERP_TWEEN: + case InputTypeBase::CUSTOM_INTERP_TWEEN_MS: return mInput.mTween.Next(); case InputTypeBase::SAMPLED: case InputTypeBase::CONSTANT_ARRAY: @@ -207,10 +207,10 @@ namespace l::nodegraph { switch (mType) { case InputTypeBase::INTERP_RWA: case InputTypeBase::INTERP_RWA_MS: - case InputTypeBase::CUSTOM_VALUE_INTERP_RWA_MS: + case InputTypeBase::CUSTOM_INTERP_RWA_MS: return mInput.mFilterRWA.Value(); - case InputTypeBase::INTERP_TWEEN: - case InputTypeBase::INTERP_TWEEN_MS: + case InputTypeBase::CUSTOM_INTERP_TWEEN: + case InputTypeBase::CUSTOM_INTERP_TWEEN_MS: return mInput.mTween.Value(); case InputTypeBase::SAMPLED: case InputTypeBase::CONSTANT_VALUE: @@ -239,11 +239,11 @@ namespace l::nodegraph { switch (mType) { case InputTypeBase::INTERP_RWA: case InputTypeBase::INTERP_RWA_MS: - case InputTypeBase::CUSTOM_VALUE_INTERP_RWA_MS: + case InputTypeBase::CUSTOM_INTERP_RWA_MS: mInput.mFilterRWA.SetRWAUpdateRate(updateRate); break; - case InputTypeBase::INTERP_TWEEN: - case InputTypeBase::INTERP_TWEEN_MS: + case InputTypeBase::CUSTOM_INTERP_TWEEN: + case InputTypeBase::CUSTOM_INTERP_TWEEN_MS: break; case InputTypeBase::SAMPLED: mInput.mIterator = input.at(mInputIndex).GetBufferIterator(numSamples); @@ -265,11 +265,11 @@ namespace l::nodegraph { mInput.mFilterRWA.SetRWAUpdateRate(updateRate); mInput.mFilterRWA.SetTarget(input.at(mInputIndex).Get()); break; - case InputTypeBase::INTERP_TWEEN: - case InputTypeBase::INTERP_TWEEN_MS: + case InputTypeBase::CUSTOM_INTERP_TWEEN: + case InputTypeBase::CUSTOM_INTERP_TWEEN_MS: mInput.mTween.Update(); break; - case InputTypeBase::CUSTOM_VALUE_INTERP_RWA_MS: // must set it manually + case InputTypeBase::CUSTOM_INTERP_RWA_MS: // must set it manually mInput.mFilterRWA.SetRWAUpdateRate(updateRate); break; case InputTypeBase::SAMPLED: diff --git a/packages/nodegraph/source/common/operations/NodeGraphOpFilter.cpp b/packages/nodegraph/source/common/operations/NodeGraphOpFilter.cpp index 220e1317..65f90a24 100644 --- a/packages/nodegraph/source/common/operations/NodeGraphOpFilter.cpp +++ b/packages/nodegraph/source/common/operations/NodeGraphOpFilter.cpp @@ -12,16 +12,12 @@ namespace l::nodegraph { /* Stateful filtering operations */ /*********************************************************************/ void GraphFilterBase::Reset() { - mReset = 0.0f; + mSync = 0.0f; mSamplesUntilUpdate = 0.0f; - mCutoff = 0.0f; - mResonance = 0.0f; - mCutoffCPS = 0.0f; - mNode->SetInput(0, 0.0f); - mNode->SetInput(2, 1.0f); - mNode->SetInput(3, 0.0f); + mNode->SetInput(2, 0.5f); + mNode->SetInput(3, 0.5f); mNode->SetInputBound(0, InputBound::INPUT_0_TO_1); mNode->SetInputBound(2, InputBound::INPUT_0_TO_1); mNode->SetInputBound(3, InputBound::INPUT_0_TO_1); @@ -31,22 +27,17 @@ namespace l::nodegraph { } void GraphFilterBase::Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) { - auto input = &inputs.at(1).Get(numSamples); + mNodeInputManager.ProcessUpdate(inputs, numSamples, mUpdateRate); + mSync = inputs.at(0).Get(); + if (mSync > 0.5f) { + mNode->SetInput(1, 0.0f, numSamples); + } + auto output = &outputs.at(0).Get(numSamples); mSamplesUntilUpdate = l::audio::BatchUpdate(mUpdateRate, mSamplesUntilUpdate, 0, numSamples, [&]() { - mReset = inputs.at(0).Get(); - if (mReset > 0.5f) { - mNode->SetInput(1, 0.0f, numSamples); - } - - mCutoff = inputs.at(2).Get(); - mCutoffCPS = l::math::functions::sin(l::math::constants::PI_f * mCutoff * mCutoff * 0.5f); - mResonance = 1.0f - inputs.at(3).Get(); - - mCutoffFilter.SetConvergenceFactor().SetTarget(mCutoffCPS).SnapAt(); - mResonanceFilter.SetConvergenceFactor().SetTarget(mResonance).SnapAt(); + mNodeInputManager.NodeUpdate(inputs, mUpdateRate); UpdateSignal(inputs, outputs); @@ -54,8 +45,8 @@ namespace l::nodegraph { }, [&](int32_t start, int32_t end, bool) { for (int32_t i = start; i < end; i++) { - float inputValue = *input++; - float signal = ProcessSignal(inputValue, mCutoffFilter.Next(), mResonanceFilter.Next()); + float inputValue = mNodeInputManager.GetValueNext(1); + float signal = ProcessSignal(inputValue, mNodeInputManager.GetValueNext(2), mNodeInputManager.GetValueNext(3)); *output++ = signal; } } @@ -114,7 +105,7 @@ namespace l::nodegraph { void GraphFilterChamberlain2pole::UpdateSignal(std::vector& inputs, std::vector&) { mMode = static_cast(3.0f * inputs.at(mNumDefaultInputs + 0).Get() + 0.5f); - mScale = l::math::functions::sqrt(mResonance); + mScale = l::math::functions::sqrt(mNodeInputManager.GetValue(3)); mScaleFilter.SetConvergenceFactor().SetTarget(mScale).SnapAt(); } @@ -122,6 +113,7 @@ namespace l::nodegraph { float inputValueInbetween = (mInputValuePrev + input) * 0.5f; float scale = mScaleFilter.Next(); cutoff *= 0.5f; + resonance = 1.0f - resonance; for (int32_t oversample = 0; oversample < 2; oversample++) { mState.at(0) = mState.at(0) + cutoff * mState.at(2); mState.at(1) = scale * (oversample == 0 ? inputValueInbetween : input) - mState.at(0) - resonance * mState.at(2); From 4d7c0797e7cbbf28c82589751e60136e00c9d751 Mon Sep 17 00:00:00 2001 From: lnd3 Date: Thu, 19 Sep 2024 21:08:49 +0200 Subject: [PATCH 27/42] Fix envelope logic. Add tests for tween and rwa since they tend to be unruly. --- packages/audio/include/audio/AudioUtils.h | 4 +- packages/audio/source/common/AudioUtils.cpp | 1 + packages/math/include/math/MathTween.h | 14 +-- packages/math/source/common/MathTween.cpp | 40 +++++++-- packages/math/tests/common/MathTweenTest.cpp | 90 +++++++++++++++++++ .../nodegraph/operations/NodeGraphOpControl.h | 10 ++- .../nodegraph/operations/NodeGraphOpSignal.h | 6 +- .../source/common/core/NodeGraphBase.cpp | 4 +- .../source/common/core/NodeGraphInput.cpp | 2 +- .../common/operations/NodeGraphOpControl.cpp | 75 ++++++++-------- 10 files changed, 185 insertions(+), 61 deletions(-) create mode 100644 packages/math/tests/common/MathTweenTest.cpp diff --git a/packages/audio/include/audio/AudioUtils.h b/packages/audio/include/audio/AudioUtils.h index a087fa0b..bcec4a50 100644 --- a/packages/audio/include/audio/AudioUtils.h +++ b/packages/audio/include/audio/AudioUtils.h @@ -68,8 +68,8 @@ namespace l::audio { return *this; } - FilterRWA& SetRWAUpdateRate(T rwaUpdateRate = static_cast(1.0)) { - mRWAUpdateRate = l::math::functions::max(rwaUpdateRate, static_cast(1.0)); + FilterRWA& SetRWAUpdateRate(T fetchesPerUpdate = static_cast(1.0)) { + mRWAUpdateRate = l::math::functions::max(fetchesPerUpdate, static_cast(1.0)); return *this; } diff --git a/packages/audio/source/common/AudioUtils.cpp b/packages/audio/source/common/AudioUtils.cpp index 8fd5c240..17c5b259 100644 --- a/packages/audio/source/common/AudioUtils.cpp +++ b/packages/audio/source/common/AudioUtils.cpp @@ -43,6 +43,7 @@ namespace l::audio { } float BatchUpdate(float updateSamples, float samplesLeft, int32_t start, int32_t end, std::function update, std::function process) { + updateSamples = l::math::functions::max(updateSamples, 1.0f); float startNum = static_cast(start); while (startNum < static_cast(end)) { bool updated = false; diff --git a/packages/math/include/math/MathTween.h b/packages/math/include/math/MathTween.h index e642c8c6..1f6fca37 100644 --- a/packages/math/include/math/MathTween.h +++ b/packages/math/include/math/MathTween.h @@ -15,6 +15,8 @@ namespace l::math::tween { RecentWeightedAverage() = default; ~RecentWeightedAverage() = default; + void Reset(float value); + void SetValue(float value); void SetConvergence(float smooth); void SetTarget(float target); float Next(); @@ -29,21 +31,23 @@ namespace l::math::tween { class DynamicTween { public: - DynamicTween(int32_t updateCount = 0, float dynamicSmooth = 0.25f) : - mUpdateCount(updateCount), - mDynamicSmooth(dynamicSmooth) + DynamicTween(float dynamicSmoothAccuracy = 0.35f) : + mDynamicSmoothAccuracy(dynamicSmoothAccuracy) {} ~DynamicTween() = default; - void Reset(float value = 0.0f, bool ignoreDynamicSmooth = false); + void Reset(float value); + void SetValue(float value, bool setSmooth = true); void SetTweenLength(int32_t tweenCount); void SetTarget(float target, int32_t tweenCount = 0); - void Update(); + void Update(float updateRate = 1.0f); float Next(); float Value(); protected: + float mUpdateRate = 0.0f; int32_t mUpdateCount = 1; + float mDynamicSmoothAccuracy = 0.1f; float mDynamicSmooth = 0.25f; float mValue = 0.0f; diff --git a/packages/math/source/common/MathTween.cpp b/packages/math/source/common/MathTween.cpp index 71f64b6b..da800091 100644 --- a/packages/math/source/common/MathTween.cpp +++ b/packages/math/source/common/MathTween.cpp @@ -6,7 +6,16 @@ namespace l::math::tween { float GetRWAFactor(int32_t steps, float limit) { - return 1.0f - l::math::functions::pow(l::math::constants::E_f, l::math::functions::log(limit) / static_cast(steps)); + return 1.0f - l::math::functions::pow(l::math::constants::E_f, l::math::functions::log(limit) / static_cast(steps-1)); + } + + void RecentWeightedAverage::Reset(float value) { + mTargetValue = value; + mValue = value; + } + + void RecentWeightedAverage::SetValue(float value) { + mValue = value; } void RecentWeightedAverage::SetConvergence(float smooth) { @@ -28,13 +37,21 @@ namespace l::math::tween { /**********************************************************************************/ - void DynamicTween::Reset(float value, bool ignoreDynamicSmooth) { + void DynamicTween::Reset(float value) { + mTargetValue = value; + mTargetValuePrev = value; + mTarget = value; + mValue = value; + mCounter = 0; + } + + void DynamicTween::SetValue(float value, bool setSmooth) { mTargetValue = value; mTargetValuePrev = value; - if (ignoreDynamicSmooth) { + if (setSmooth) { + mTarget = value; mValue = value; } - mCounter = 0; } void DynamicTween::SetTweenLength(int32_t tweenCount) { @@ -45,15 +62,23 @@ namespace l::math::tween { mTargetValuePrev = mValue; mTargetValue = target; mCounter = 0; - mUpdateCount = l::math::functions::max(tweenCount, 4); + if (tweenCount > 4) { + mUpdateCount = l::math::functions::max(tweenCount, 4); + } } // update rate in audio processing, the number of samples between calls // we can use this to update expensive interpolation here - void DynamicTween::Update() { + void DynamicTween::Update(float updateRate) { + if (updateRate != mUpdateRate) { + mUpdateRate = l::math::functions::max(updateRate, 2.0f); + mDynamicSmooth = GetRWAFactor(static_cast(mUpdateRate), mDynamicSmoothAccuracy); + } + mTarget = mTargetValue; if (mCounter < mUpdateCount) { - float t = mCounter / static_cast(mUpdateCount); + float t = (mCounter + 0.5f * mUpdateRate) / static_cast(mUpdateCount - 0.5f * mUpdateRate); + t = l::math::functions::clamp(t, 0.0f, 1.0f); mTarget = mTargetValuePrev + l::math::smooth::smoothPolyh3(t) * (mTargetValue - mTargetValuePrev); } } @@ -65,7 +90,6 @@ namespace l::math::tween { } float DynamicTween::Value() { - mValue += mDynamicSmooth * (mTarget - mValue); return mValue; } } diff --git a/packages/math/tests/common/MathTweenTest.cpp b/packages/math/tests/common/MathTweenTest.cpp new file mode 100644 index 00000000..d42ec505 --- /dev/null +++ b/packages/math/tests/common/MathTweenTest.cpp @@ -0,0 +1,90 @@ +#include "testing/Test.h" +#include "logging/Log.h" + +#include "math/MathAll.h" + +using namespace l; +using namespace l::math::tween; + +TEST(MathTween, RecentWeightedAverage) { + RecentWeightedAverage rwa; + + rwa.SetConvergence(GetRWAFactor(10, 0.1f)); + rwa.SetValue(1.0f); + rwa.SetTarget(0.0f); + TEST_FUZZY(rwa.Value(), 1.0f, 0.1f, ""); + for (int i = 0; i < 10; i++) { + auto value = rwa.Next(); + LOG(LogInfo) << "RWA: " << value; + } + TEST_FUZZY(rwa.Value(), 0.0f, 0.1f, ""); + + return 0; +} + +TEST(MathTween, DynamicTween) { + DynamicTween tween(0.25f); + tween.SetValue(1.0f); + tween.SetTarget(0.0, 10); + TEST_FUZZY(tween.Value(), 1.0f, 0.1f, ""); + for (int i = 0; i < 10; i++) { + tween.Update(); + auto value = tween.Next(); + LOG(LogInfo) << "Tween: " << value; + } + TEST_FUZZY(tween.Value(), 0.0f, 0.01f, ""); + + return 0; +} + +TEST(MathTween, DynamicTweenVeryShortBatch) { + DynamicTween tween(0.1f); + tween.SetValue(1.0f); + tween.SetTarget(0.0, 22); + TEST_FUZZY(tween.Value(), 1.0f, 0.01f, ""); + for (int i = 0; i < 30; i++) { + if (i % 10 == 0) { + tween.Update(10); + } + auto value = tween.Next(); + LOG(LogInfo) << "Tween (" << i << "): " << value; + } + TEST_FUZZY(tween.Value(), 0.0f, 0.01f, ""); + + return 0; +} + +TEST(MathTween, DynamicTweenShortBatch) { + DynamicTween tween(0.35f); + tween.SetValue(1.0f); + tween.SetTarget(0.0, 102); + TEST_FUZZY(tween.Value(), 1.0f, 0.01f, ""); + for (int i = 0; i < 110; i++) { + if (i % 10 == 0) { + tween.Update(10); + } + auto value = tween.Next(); + LOG(LogInfo) << "Tween (" << i << "): " << value; + } + TEST_FUZZY(tween.Value(), 0.0f, 0.01f, ""); + + return 0; +} + +TEST(MathTween, DynamicTweenLongBatch) { + int32_t updateRate = 100; + DynamicTween tween(0.35f); + tween.SetValue(1.0f); + tween.SetTarget(0.0, 1020); + TEST_FUZZY(tween.Value(), 1.0f, 0.01f, ""); + for (int i = 0; i < 1100; i++) { + if (i % 10 == 0) { + tween.Update(updateRate); + } + auto value = tween.Next(); + LOG(LogInfo) << "Tween (" << i << "): " << value; + } + TEST_FUZZY(tween.Value(), 0.0f, 0.01f, ""); + + return 0; +} diff --git a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpControl.h b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpControl.h index 5e560c18..2a426a37 100644 --- a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpControl.h +++ b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpControl.h @@ -34,8 +34,8 @@ namespace l::nodegraph { { mNodeInputManager.AddInputBase(InputTypeBase::CONSTANT_VALUE, AddInput("Sync", 0.0f, 1, 0.0f, 1.0f)); mNodeInputManager.AddInputBase(InputTypeBase::CONSTANT_VALUE, AddInput("Rate", 256.0f, 1, 1.0f, 2048.0f)); - mNodeInputManager.AddInputBase(InputTypeBase::CUSTOM_INTERP_TWEEN, AddInput("Velocity", 0.5f, 1, 0.0f, 1.0f)); - mNodeInputManager.AddInputBase(InputTypeBase::CUSTOM_INTERP_TWEEN, AddInput("Fade", 0.1f, 1, 0.0001f, 1.0f)); + mNodeInputManager.AddInputBase(InputTypeBase::CONSTANT_VALUE, AddInput("Velocity", 0.5f, 1, 0.0f, 1.0f)); + mNodeInputManager.AddInputBase(InputTypeBase::CONSTANT_VALUE, AddInput("Fade", 0.1f, 1, 0.0001f, 1.0f)); mNodeInputManager.AddInputBase(InputTypeBase::CONSTANT_VALUE, AddInput("Attack", 50.0f, 1, 1.0f, 10000.0f)); mNodeInputManager.AddInputBase(InputTypeBase::CONSTANT_VALUE, AddInput("Release", 50.0f, 1, 1.0f, 10000.0f)); @@ -50,12 +50,11 @@ namespace l::nodegraph { virtual void UpdateSignal(NodeInputManager& inputManager) = 0; virtual std::pair ProcessSignal(NodeInputManager& inputManager) = 0; protected: + NodeInputManager mNodeInputManager; float mUpdateRate = 256.0f; float mDeltaTime = 0.0f; float mSamplesUntilUpdate = 0.0f; - - NodeInputManager mNodeInputManager; }; /*********************************************************************/ @@ -66,6 +65,7 @@ namespace l::nodegraph { { mFreqTargetId = mNodeInputManager.AddInputBase(InputTypeBase::CONSTANT_VALUE, AddInput("Freq")); mFreqId = mNodeInputManager.AddInputBase(InputTypeBase::CUSTOM_INTERP_RWA_MS); + mNodeInputManager.AddInputBase(InputTypeBase::CUSTOM_INTERP_TWEEN_MS); mEnvelope = 0.0f; } @@ -73,6 +73,7 @@ namespace l::nodegraph { virtual void UpdateSignal(NodeInputManager& inputManager) override; virtual std::pair ProcessSignal(NodeInputManager& inputManager) override; protected: + bool mNoteOn = false; int32_t mFrameCount = 0; int32_t mFreqTargetId = 0; int32_t mFreqId = 0; @@ -82,6 +83,7 @@ namespace l::nodegraph { float mReleaseFactor = 0.0f; float mEnvelopeTarget = 0.0f; float mFreqTarget = 0.0f; + float mFreqTargetPrev = 0.0f; float mEnvelope = 0.0f; }; diff --git a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpSignal.h b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpSignal.h index 02d57a01..81e44b73 100644 --- a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpSignal.h +++ b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpSignal.h @@ -34,9 +34,9 @@ namespace l::nodegraph { { mNodeInputManager.AddInputBase(InputTypeBase::CONSTANT_VALUE, AddInput("Sync", 0.0f, 1, 0.0f, 1.0f)); mNodeInputManager.AddInputBase(InputTypeBase::CONSTANT_VALUE, AddInput("Rate", 256.0f, 1, 1.0f, 2048.0f)); - mNodeInputManager.AddInputBase(InputTypeBase::CUSTOM_INTERP_TWEEN, AddInput("Freq", 0.0f, 1, 0.0f, 22050.0f)); - mNodeInputManager.AddInputBase(InputTypeBase::CUSTOM_INTERP_TWEEN, AddInput("Volume", 0.5f, 1, 0.0f, 5.0f)); - mNodeInputManager.AddInputBase(InputTypeBase::CUSTOM_INTERP_TWEEN, AddInput("Smooth", 1.0f, 1, 0.0f, 1.0f)); + mNodeInputManager.AddInputBase(InputTypeBase::INTERP_RWA, AddInput("Freq", 0.0f, 1, 0.0f, 22050.0f)); + mNodeInputManager.AddInputBase(InputTypeBase::INTERP_RWA, AddInput("Volume", 0.5f, 1, 0.0f, 5.0f)); + mNodeInputManager.AddInputBase(InputTypeBase::INTERP_RWA, AddInput("Smooth", 1.0f, 1, 0.0f, 1.0f)); AddOutput("Out", 0.0f, 2); diff --git a/packages/nodegraph/source/common/core/NodeGraphBase.cpp b/packages/nodegraph/source/common/core/NodeGraphBase.cpp index 9df32d67..d66c8ed3 100644 --- a/packages/nodegraph/source/common/core/NodeGraphBase.cpp +++ b/packages/nodegraph/source/common/core/NodeGraphBase.cpp @@ -373,14 +373,14 @@ namespace l::nodegraph { /**********************************************************************************/ int32_t NodeInputManager::AddInputBase(InputTypeBase type, int32_t inputIndex) { - if (type != InputTypeBase::CUSTOM_INTERP_RWA_MS) { + if (type != InputTypeBase::CUSTOM_INTERP_RWA_MS && type != InputTypeBase::CUSTOM_INTERP_TWEEN && type != InputTypeBase::CUSTOM_INTERP_TWEEN_MS) { ASSERT(inputIndex >= 0); } inputIndex = static_cast(mInputs.size()); mInputs.emplace_back(NodeGraphInputAccessor{ type, inputIndex }); - if (type != InputTypeBase::CUSTOM_INTERP_RWA_MS) { + if (type != InputTypeBase::CUSTOM_INTERP_RWA_MS && type != InputTypeBase::CUSTOM_INTERP_TWEEN && type != InputTypeBase::CUSTOM_INTERP_TWEEN_MS) { mInputs.back().SetValue(mNodeGraphOperation.GetDefaultData(static_cast(inputIndex))); mInputs.back().SetTarget(mNodeGraphOperation.GetDefaultData(static_cast(inputIndex))); } diff --git a/packages/nodegraph/source/common/core/NodeGraphInput.cpp b/packages/nodegraph/source/common/core/NodeGraphInput.cpp index 5470eb32..fdf7ce5a 100644 --- a/packages/nodegraph/source/common/core/NodeGraphInput.cpp +++ b/packages/nodegraph/source/common/core/NodeGraphInput.cpp @@ -267,7 +267,7 @@ namespace l::nodegraph { break; case InputTypeBase::CUSTOM_INTERP_TWEEN: case InputTypeBase::CUSTOM_INTERP_TWEEN_MS: - mInput.mTween.Update(); + mInput.mTween.Update(updateRate); break; case InputTypeBase::CUSTOM_INTERP_RWA_MS: // must set it manually mInput.mFilterRWA.SetRWAUpdateRate(updateRate); diff --git a/packages/nodegraph/source/common/operations/NodeGraphOpControl.cpp b/packages/nodegraph/source/common/operations/NodeGraphOpControl.cpp index 7058fa1d..873be5c4 100644 --- a/packages/nodegraph/source/common/operations/NodeGraphOpControl.cpp +++ b/packages/nodegraph/source/common/operations/NodeGraphOpControl.cpp @@ -49,69 +49,72 @@ namespace l::nodegraph { mFreqTarget = inputManager.GetValueNext(mFreqTargetId); inputManager.SetTarget(mFreqId, mFreqTarget); + inputManager.SetDuration(mFreqId, 100.0f * inputManager.GetValue(3)); mAttackFrames = l::audio::GetAudioTicksFromMS(inputManager.GetValue(4)); mReleaseFrames = l::audio::GetAudioTicksFromMS(inputManager.GetValue(5)); - mAttackFactor = l::audio::GetRWAFactorFromMSSkewed(inputManager.GetValue(4), 0.0001f); - mReleaseFactor = l::audio::GetRWAFactorFromMS(inputManager.GetValue(5), 0.0001f); + mAttackFactor = inputManager.GetValue(4); + mReleaseFactor = inputManager.GetValue(5); } std::pair GraphControlEnvelope::ProcessSignal(NodeInputManager& inputManager) { float velocity = inputManager.GetValueNext(2); - float freq = inputManager.GetValue(mFreqId); + float freq = inputManager.GetValueNext(mFreqId); + + bool noteOn = mFreqTarget != 0.0f; + bool differentNote = mNoteOn && noteOn && mFreqTargetPrev != mFreqTarget; + if (noteOn && !mNoteOn || differentNote) { + mNoteOn = true; + + mFreqTargetPrev = mFreqTarget; + + mNodeInputManager.SetDuration(8, mAttackFactor); + mNodeInputManager.SetTarget(8, velocity); + + if (mFrameCount == 0) { + // if note was off we set freq immediately + freq = mFreqTarget; + } + mFrameCount = 0; + } + else if (!noteOn && mNoteOn) { + mNoteOn = false; + + mNodeInputManager.SetDuration(8, mReleaseFactor); + mNodeInputManager.SetTarget(8, 0.0f); - if (mFreqTarget == 0.0f) { - // trigger release if (mFrameCount > 0 && mFrameCount < mAttackFrames + 1) { + // still in attack so fast forward to release mFrameCount = mAttackFrames + 2; } else if (mFrameCount > mAttackFrames + 1 + mReleaseFrames) { + // if released, reset mFrameCount = 0; } } - else { - if (mFrameCount == 0) { - freq = mFreqTarget; + + if (mNoteOn) { + if (mFrameCount < mAttackFrames) { + // attack + mFrameCount++; } - else if (mFreqTarget != 0) { - mFrameCount = mAttackFrames + 2; - mEnvelopeTarget = velocity; + else if (mFrameCount == mAttackFrames + 1) { + // sustain, linger on frame after last attack frame } } - - if (mFreqTarget != 0 && mFrameCount < mAttackFrames) { - // attack - mEnvelopeTarget = velocity; - mFrameCount++; - } - else if (mFreqTarget != 0 && mFrameCount == mAttackFrames + 1) { - // sustain - } - else if (mFreqTarget == 0 && mFrameCount > mAttackFrames + 1) { + else if (mFrameCount > 0) { // release - mEnvelopeTarget = 0.0f; mFrameCount++; if (mFrameCount > mAttackFrames + 1 + mReleaseFrames) { mFrameCount = 0; } } - float delta = mEnvelopeTarget - mEnvelope; - if (delta > 0) { - mEnvelope += mAttackFactor * delta; - } - else { - mEnvelope += mReleaseFactor * delta; - } - - if (mFreqTarget != 0.0f) { - // note on + if (mNoteOn) { freq = inputManager.GetValueNext(mFreqId); } - else { - // note off - } - return { freq, mEnvelope }; + float envelope = mNodeInputManager.GetValueNext(8); + return { freq, envelope }; } /*********************************************************************/ From 43f7b7eec2d2be181a63e0884a0ebb76ab14a8aa Mon Sep 17 00:00:00 2001 From: lnd3 Date: Fri, 20 Sep 2024 01:28:28 +0200 Subject: [PATCH 28/42] Fix some of the errors with envelope and arpeggio. --- packages/audio/source/common/AudioUtils.cpp | 8 ++--- packages/math/source/common/MathTween.cpp | 5 ++- .../include/nodegraph/core/NodeGraphBase.h | 2 +- .../include/nodegraph/core/NodeGraphInput.h | 2 +- .../nodegraph/operations/NodeGraphOpControl.h | 8 +++-- .../nodegraph/operations/NodeGraphOpSignal.h | 15 ++++---- .../source/common/core/NodeGraphBase.cpp | 4 +-- .../source/common/core/NodeGraphInput.cpp | 8 ++--- .../common/operations/NodeGraphOpControl.cpp | 33 +++++++++-------- .../common/operations/NodeGraphOpSignal.cpp | 36 ++++++------------- 10 files changed, 58 insertions(+), 63 deletions(-) diff --git a/packages/audio/source/common/AudioUtils.cpp b/packages/audio/source/common/AudioUtils.cpp index 17c5b259..647cea5d 100644 --- a/packages/audio/source/common/AudioUtils.cpp +++ b/packages/audio/source/common/AudioUtils.cpp @@ -29,14 +29,14 @@ namespace l::audio { return numAudioSamples * 1000.0f / sampleRate; } - float GetRWAFactorFromMS(float ms, float limit, float rwaUpdateRate, float sampleRate) { - int32_t updateSteps = GetAudioTicksFromMS(ms, sampleRate / rwaUpdateRate); + float GetRWAFactorFromMS(float ms, float limit, float , float sampleRate) { + int32_t updateSteps = GetAudioTicksFromMS(ms, sampleRate); return l::math::tween::GetRWAFactor(updateSteps, limit); } - float GetRWAFactorFromMSSkewed(float ms, float limit, float rwaUpdateRate, float sampleRate) { + float GetRWAFactorFromMSSkewed(float ms, float limit, float, float sampleRate) { float msRoot = l::math::functions::sqrt(ms); - int32_t steps = GetAudioTicksFromMS(msRoot, sampleRate / rwaUpdateRate); + int32_t steps = GetAudioTicksFromMS(msRoot, sampleRate); float factor = l::math::tween::GetRWAFactor(steps, limit); factor *= factor; return factor; diff --git a/packages/math/source/common/MathTween.cpp b/packages/math/source/common/MathTween.cpp index da800091..d13c658c 100644 --- a/packages/math/source/common/MathTween.cpp +++ b/packages/math/source/common/MathTween.cpp @@ -6,6 +6,9 @@ namespace l::math::tween { float GetRWAFactor(int32_t steps, float limit) { + if (steps < 2) { + return 1.0f; + } return 1.0f - l::math::functions::pow(l::math::constants::E_f, l::math::functions::log(limit) / static_cast(steps-1)); } @@ -62,7 +65,7 @@ namespace l::math::tween { mTargetValuePrev = mValue; mTargetValue = target; mCounter = 0; - if (tweenCount > 4) { + if (tweenCount >= 4) { mUpdateCount = l::math::functions::max(tweenCount, 4); } } diff --git a/packages/nodegraph/include/nodegraph/core/NodeGraphBase.h b/packages/nodegraph/include/nodegraph/core/NodeGraphBase.h index f2a70a8e..9b4e7bf4 100644 --- a/packages/nodegraph/include/nodegraph/core/NodeGraphBase.h +++ b/packages/nodegraph/include/nodegraph/core/NodeGraphBase.h @@ -237,7 +237,7 @@ namespace l::nodegraph { float GetValue(int32_t inputIndex); float GetArrayValue(int32_t inputIndex, int32_t arrayIndex); float* GetArray(int32_t inputIndex); - void SetDuration(int32_t inputIndex, float value); + void SetDuration(int32_t inputIndex, float value, float limit = 0.001f); void SetTarget(int32_t inputIndex, float value); void SetValue(int32_t inputIndex, float value); diff --git a/packages/nodegraph/include/nodegraph/core/NodeGraphInput.h b/packages/nodegraph/include/nodegraph/core/NodeGraphInput.h index 512a7a50..a6717d52 100644 --- a/packages/nodegraph/include/nodegraph/core/NodeGraphInput.h +++ b/packages/nodegraph/include/nodegraph/core/NodeGraphInput.h @@ -155,7 +155,7 @@ namespace l::nodegraph { } ~NodeGraphInputAccessor() = default; - void SetDuration(float ms); + void SetDuration(float ms, float limit = 0.001f); void SetDuration(int32_t ticks); void SetTarget(float value); void SetValue(float value); diff --git a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpControl.h b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpControl.h index 2a426a37..64772469 100644 --- a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpControl.h +++ b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpControl.h @@ -99,11 +99,13 @@ namespace l::nodegraph { mNodeInputManager.AddInputBase(InputTypeBase::CONSTANT_VALUE, AddInput("Sync", 0.0f, 1, 0.0f, 1.0f)); mNodeInputManager.AddInputBase(InputTypeBase::CONSTANT_VALUE, AddInput("Bpm", 60.0f, 1, 1.0f, 1000.0f)); mNodeInputManager.AddInputBase(InputTypeBase::CONSTANT_VALUE, AddInput("Velocity", 0.5f, 1, 0.0f, 1.0f)); - mNodeInputManager.AddInputBase(InputTypeBase::CONSTANT_VALUE, AddInput("Fade", 0.1f, 1, 0.0001f, 1.0f)); - mNodeInputManager.AddInputBase(InputTypeBase::CONSTANT_VALUE, AddInput("Attack", 50.0f, 1, 1.0f, 10000.0f)); - mNodeInputManager.AddInputBase(InputTypeBase::CONSTANT_VALUE, AddInput("Release", 50.0f, 1, 1.0f, 10000.0f)); + mNodeInputManager.AddInputBase(InputTypeBase::CONSTANT_VALUE, AddInput("Fade", 1.0f, 1, 0.0001f, 1.0f)); + mNodeInputManager.AddInputBase(InputTypeBase::CONSTANT_VALUE, AddInput("Attack", 5.0f, 1, 1.0f, 10000.0f)); + mNodeInputManager.AddInputBase(InputTypeBase::CONSTANT_VALUE, AddInput("Release", 20.0f, 1, 1.0f, 10000.0f)); mNodeInputManager.AddInputBase(InputTypeBase::CONSTANT_ARRAY, AddInput("Note On", l::audio::gNoNote_f, gPolyphony, -499.0, 500.0)); mNodeInputManager.AddInputBase(InputTypeBase::CONSTANT_ARRAY, AddInput("Note Off", l::audio::gNoNote_f, gPolyphony, -499.0, 500.0)); + mNodeInputManager.AddInputBase(InputTypeBase::CUSTOM_INTERP_RWA_MS); + mNodeInputManager.AddInputBase(InputTypeBase::CUSTOM_INTERP_RWA_MS); AddOutput("Freq"); AddOutput("Volume"); diff --git a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpSignal.h b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpSignal.h index 81e44b73..f071188f 100644 --- a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpSignal.h +++ b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpSignal.h @@ -34,9 +34,9 @@ namespace l::nodegraph { { mNodeInputManager.AddInputBase(InputTypeBase::CONSTANT_VALUE, AddInput("Sync", 0.0f, 1, 0.0f, 1.0f)); mNodeInputManager.AddInputBase(InputTypeBase::CONSTANT_VALUE, AddInput("Rate", 256.0f, 1, 1.0f, 2048.0f)); - mNodeInputManager.AddInputBase(InputTypeBase::INTERP_RWA, AddInput("Freq", 0.0f, 1, 0.0f, 22050.0f)); - mNodeInputManager.AddInputBase(InputTypeBase::INTERP_RWA, AddInput("Volume", 0.5f, 1, 0.0f, 5.0f)); - mNodeInputManager.AddInputBase(InputTypeBase::INTERP_RWA, AddInput("Smooth", 1.0f, 1, 0.0f, 1.0f)); + mNodeInputManager.AddInputBase(InputTypeBase::SAMPLED, AddInput("Freq", 0.0f, 1, 0.0f, 22050.0f)); + mNodeInputManager.AddInputBase(InputTypeBase::SAMPLED, AddInput("Volume", 0.0f, 1, 0.0f, 5.0f)); + mNodeInputManager.AddInputBase(InputTypeBase::CONSTANT_VALUE, AddInput("Smooth", 1.0f, 1, 0.0f, 1.0f)); AddOutput("Out", 0.0f, 2); @@ -67,7 +67,6 @@ namespace l::nodegraph { float mSamplesUntilUpdate = 0.0f; float mUpdateRate = 16.0f; - l::audio::FilterRWA mFilterSignal; l::audio::FilterRWA mFilterVolume; }; @@ -77,8 +76,8 @@ namespace l::nodegraph { GraphSignalSine2(NodeGraphBase* node) : GraphSignalBase(node, "Sine 2") { - AddInput("Fmod", 0.0f, 1, 0.0f, 1.0f); - AddInput("Phase", 0.0f, 1, 0.0f, 1.0f); + mNodeInputManager.AddInputBase(InputTypeBase::INTERP_RWA, AddInput("Fmod", 0.0f, 1, 0.0f, 1.0f)); + mNodeInputManager.AddInputBase(InputTypeBase::INTERP_RWA, AddInput("Phase", 0.0f, 1, 0.0f, 1.0f)); } virtual ~GraphSignalSine2() = default; @@ -120,8 +119,8 @@ namespace l::nodegraph { GraphSignalSaw2(NodeGraphBase* node) : GraphSignalBase(node, "Saw 2") { - AddInput("Attenuation", 0.0f, 1, 0.0f, 1.0f); - AddInput("Cutoff", 0.0f, 1, 0.0f, 1.0f); + mNodeInputManager.AddInputBase(InputTypeBase::INTERP_RWA, AddInput("Attenuation", 0.0f, 1, 0.0f, 1.0f)); + mNodeInputManager.AddInputBase(InputTypeBase::INTERP_RWA, AddInput("Cutoff", 0.0f, 1, 0.0f, 1.0f)); } virtual ~GraphSignalSaw2() = default; diff --git a/packages/nodegraph/source/common/core/NodeGraphBase.cpp b/packages/nodegraph/source/common/core/NodeGraphBase.cpp index d66c8ed3..33c0d158 100644 --- a/packages/nodegraph/source/common/core/NodeGraphBase.cpp +++ b/packages/nodegraph/source/common/core/NodeGraphBase.cpp @@ -415,8 +415,8 @@ namespace l::nodegraph { return mInputs.at(inputIndex).GetArray(); } - void NodeInputManager::SetDuration(int32_t inputIndex, float value) { - mInputs.at(inputIndex).SetDuration(value); + void NodeInputManager::SetDuration(int32_t inputIndex, float value, float limit) { + mInputs.at(inputIndex).SetDuration(value, limit); } void NodeInputManager::SetTarget(int32_t inputIndex, float value) { diff --git a/packages/nodegraph/source/common/core/NodeGraphInput.cpp b/packages/nodegraph/source/common/core/NodeGraphInput.cpp index fdf7ce5a..682d3e7b 100644 --- a/packages/nodegraph/source/common/core/NodeGraphInput.cpp +++ b/packages/nodegraph/source/common/core/NodeGraphInput.cpp @@ -128,14 +128,14 @@ namespace l::nodegraph { } } - void NodeGraphInputAccessor::SetDuration(float ms) { + void NodeGraphInputAccessor::SetDuration(float ms, float limit) { switch (mType) { case InputTypeBase::INTERP_RWA: - mInput.mFilterRWA.SetConvergenceInMs(ms); + mInput.mFilterRWA.SetConvergenceInMs(ms, limit); break; case InputTypeBase::INTERP_RWA_MS: case InputTypeBase::CUSTOM_INTERP_RWA_MS: - mInput.mFilterRWA.SetConvergenceInMs(ms); + mInput.mFilterRWA.SetConvergenceInMs(ms, limit); break; case InputTypeBase::CUSTOM_INTERP_TWEEN: case InputTypeBase::CUSTOM_INTERP_TWEEN_MS: @@ -176,7 +176,7 @@ namespace l::nodegraph { break; case InputTypeBase::CUSTOM_INTERP_TWEEN: case InputTypeBase::CUSTOM_INTERP_TWEEN_MS: - mInput.mTween.Reset(value, true); + mInput.mTween.Reset(value); break; case InputTypeBase::SAMPLED: case InputTypeBase::CONSTANT_VALUE: diff --git a/packages/nodegraph/source/common/operations/NodeGraphOpControl.cpp b/packages/nodegraph/source/common/operations/NodeGraphOpControl.cpp index 873be5c4..68bbbcdf 100644 --- a/packages/nodegraph/source/common/operations/NodeGraphOpControl.cpp +++ b/packages/nodegraph/source/common/operations/NodeGraphOpControl.cpp @@ -48,7 +48,9 @@ namespace l::nodegraph { void GraphControlEnvelope::UpdateSignal(NodeInputManager& inputManager) { mFreqTarget = inputManager.GetValueNext(mFreqTargetId); - inputManager.SetTarget(mFreqId, mFreqTarget); + if (mFreqTarget != 0.0f) { + inputManager.SetTarget(mFreqId, mFreqTarget); + } inputManager.SetDuration(mFreqId, 100.0f * inputManager.GetValue(3)); mAttackFrames = l::audio::GetAudioTicksFromMS(inputManager.GetValue(4)); mReleaseFrames = l::audio::GetAudioTicksFromMS(inputManager.GetValue(5)); @@ -58,7 +60,7 @@ namespace l::nodegraph { std::pair GraphControlEnvelope::ProcessSignal(NodeInputManager& inputManager) { float velocity = inputManager.GetValueNext(2); - float freq = inputManager.GetValueNext(mFreqId); + float freq = 0.0f; bool noteOn = mFreqTarget != 0.0f; bool differentNote = mNoteOn && noteOn && mFreqTargetPrev != mFreqTarget; @@ -68,7 +70,7 @@ namespace l::nodegraph { mFreqTargetPrev = mFreqTarget; mNodeInputManager.SetDuration(8, mAttackFactor); - mNodeInputManager.SetTarget(8, velocity); + mNodeInputManager.SetTarget(8, l::math::functions::sqrt(velocity)); if (mFrameCount == 0) { // if note was off we set freq immediately @@ -109,9 +111,7 @@ namespace l::nodegraph { } } - if (mNoteOn) { - freq = inputManager.GetValueNext(mFreqId); - } + freq = inputManager.GetValueNext(mFreqId); float envelope = mNodeInputManager.GetValueNext(8); return { freq, envelope }; @@ -138,9 +138,14 @@ namespace l::nodegraph { auto velocity = inputManager.GetValue(2); auto fade = inputManager.GetValue(3); - mGainTarget = velocity; + mGainTarget = 3.0f*velocity; mFreqSmoothing = fade; mFreqSmoothing *= mFreqSmoothing * 0.5f; + + inputManager.SetDuration(8, attack, 0.01f); + inputManager.SetDuration(9, attack + release, 0.01f); + inputManager.SetValue(8, 0.0f); + inputManager.SetValue(9, 1.0f); } } @@ -200,13 +205,13 @@ namespace l::nodegraph { }, [&](int32_t start, int32_t end, bool) { for (int32_t i = start; i < end; i++) { - float delta = mGainTarget - mGain; - if (delta > 0) { - mGain += mGainAttack * l::math::smooth::smoothPolyh3(delta); - } - else { - mGain += mGainRelease * (-l::math::smooth::smoothPolyh3(-delta)); - } + mNodeInputManager.SetTarget(8, 1.0f); + mNodeInputManager.SetTarget(9, 0.0f); + float attack = mNodeInputManager.GetValueNext(8); + float release = mNodeInputManager.GetValueNext(9); + float gain = mGainTarget * (attack * release); + mGain += 0.25f * (gain - mGain); + mFreq += mFreqSmoothing * (mFreqTarget - mFreq); *output0++ = mFreq; diff --git a/packages/nodegraph/source/common/operations/NodeGraphOpSignal.cpp b/packages/nodegraph/source/common/operations/NodeGraphOpSignal.cpp index 9b5a895b..0e71642f 100644 --- a/packages/nodegraph/source/common/operations/NodeGraphOpSignal.cpp +++ b/packages/nodegraph/source/common/operations/NodeGraphOpSignal.cpp @@ -13,7 +13,7 @@ namespace l::nodegraph { void GraphSignalBase::Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) { mNodeInputManager.ProcessUpdate(inputs, numSamples, mUpdateRate); - float sync = inputs.at(0).Get(); + float sync = mNodeInputManager.GetValueNext(0); if (sync > 0.5f) { mSamplesUntilUpdate = 0; } @@ -25,23 +25,9 @@ namespace l::nodegraph { mUpdateRate = inputs.at(1).Get(); mNodeInputManager.NodeUpdate(inputs, mUpdateRate); + mSmooth = mNodeInputManager.GetValueNext(4); - mFreq = inputs.at(2).Get(); - - if (mFreq <= 0.0f) { - mFreq = 0.0f; - mVolumeTarget *= mVolumeTarget; - } - else { - mVolumeTarget = inputs.at(3).Get(); - } - mSmooth = inputs.at(4).Get(); - - if (mFilterVolume.SetConvergenceFactor().SetTarget(mVolumeTarget).SnapAt()) { - mVolumeTarget = 0.0f; - ResetSignal(); - } - mFilterSignal.SetConvergenceFactor(mSmooth); + mNodeInputManager.SetDuration(2, 10000.0f * (1.0f - mSmooth), 0.05f); mDeltaTime = 1.0f / 44100.0f; @@ -51,8 +37,8 @@ namespace l::nodegraph { }, [&](int32_t start, int32_t end, bool) { for (int32_t i = start; i < end; i++) { - float signalTarget = ProcessSignal(mDeltaTime, mFreq); - *output0++ = mFilterVolume.Next() * mFilterSignal.SetTarget(signalTarget).Next(); + float signalTarget = ProcessSignal(mDeltaTime, mNodeInputManager.GetValueNext(2)); + *output0++ = mNodeInputManager.GetValueNext(3) * signalTarget; } } ); @@ -73,9 +59,9 @@ namespace l::nodegraph { mPhase = 0.0f; } - void GraphSignalSine2::UpdateSignal(std::vector& inputs, std::vector&) { - mFmod = inputs.at(mNumDefaultInputs + 0).Get(); - mPmod = inputs.at(mNumDefaultInputs + 1).Get(); + void GraphSignalSine2::UpdateSignal(std::vector&, std::vector&) { + mFmod = mNodeInputManager.GetValueNext(mNumDefaultInputs + 0); + mPmod = mNodeInputManager.GetValueNext(mNumDefaultInputs + 1); mFmod *= 0.25f * 0.25f * 0.5f * 44100.0f / l::math::functions::max(mFreq, 1.0f); mFilterFmod.SetConvergenceFactor().SetTarget(mFmod); @@ -114,9 +100,9 @@ namespace l::nodegraph { InitSaw(&mSaw, 0.0, 0.0); } - void GraphSignalSaw2::UpdateSignal(std::vector& inputs, std::vector&) { - mAttenuation = inputs.at(mNumDefaultInputs + 0).Get(); - mCutoff = inputs.at(mNumDefaultInputs + 1).Get(); + void GraphSignalSaw2::UpdateSignal(std::vector&, std::vector&) { + mAttenuation = mNodeInputManager.GetValueNext(mNumDefaultInputs + 0); + mCutoff = mNodeInputManager.GetValueNext(mNumDefaultInputs + 1); UpdateSaw(&mSaw, 0.00001f + 0.99999f * mAttenuation * mAttenuation * mAttenuation, mCutoff * mCutoff); } From 3c955741bd167ed381db10f073fa6c05bebb12fe Mon Sep 17 00:00:00 2001 From: lnd3 Date: Fri, 20 Sep 2024 01:34:18 +0200 Subject: [PATCH 29/42] Fix warning. --- .../nodegraph/source/common/operations/NodeGraphOpControl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/nodegraph/source/common/operations/NodeGraphOpControl.cpp b/packages/nodegraph/source/common/operations/NodeGraphOpControl.cpp index 68bbbcdf..bea78c81 100644 --- a/packages/nodegraph/source/common/operations/NodeGraphOpControl.cpp +++ b/packages/nodegraph/source/common/operations/NodeGraphOpControl.cpp @@ -64,7 +64,7 @@ namespace l::nodegraph { bool noteOn = mFreqTarget != 0.0f; bool differentNote = mNoteOn && noteOn && mFreqTargetPrev != mFreqTarget; - if (noteOn && !mNoteOn || differentNote) { + if ((noteOn && !mNoteOn) || differentNote) { mNoteOn = true; mFreqTargetPrev = mFreqTarget; From 335b8b3c92151406f7c06a5340bad6f269a9462d Mon Sep 17 00:00:00 2001 From: lnd3 Date: Fri, 20 Sep 2024 03:11:10 +0200 Subject: [PATCH 30/42] Fix signal input error. --- .../include/nodegraph/operations/NodeGraphOpSignal.h | 2 +- .../source/common/operations/NodeGraphOpSignal.cpp | 6 ++++-- packages/nodegraph/tests/common/NodeGraphBatchingTest.cpp | 3 ++- packages/nodegraph/tests/common/NodeGraphSchemaTest.cpp | 6 +++--- 4 files changed, 10 insertions(+), 7 deletions(-) diff --git a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpSignal.h b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpSignal.h index f071188f..079e0179 100644 --- a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpSignal.h +++ b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpSignal.h @@ -67,7 +67,7 @@ namespace l::nodegraph { float mSamplesUntilUpdate = 0.0f; float mUpdateRate = 16.0f; - l::audio::FilterRWA mFilterVolume; + l::audio::FilterRWA mRWAFreq; }; /*********************************************************************/ diff --git a/packages/nodegraph/source/common/operations/NodeGraphOpSignal.cpp b/packages/nodegraph/source/common/operations/NodeGraphOpSignal.cpp index 0e71642f..5063f397 100644 --- a/packages/nodegraph/source/common/operations/NodeGraphOpSignal.cpp +++ b/packages/nodegraph/source/common/operations/NodeGraphOpSignal.cpp @@ -27,7 +27,7 @@ namespace l::nodegraph { mSmooth = mNodeInputManager.GetValueNext(4); - mNodeInputManager.SetDuration(2, 10000.0f * (1.0f - mSmooth), 0.05f); + mRWAFreq.SetConvergenceInMs(1000.0f * (1.0f - mSmooth), 0.05f); mDeltaTime = 1.0f / 44100.0f; @@ -37,7 +37,9 @@ namespace l::nodegraph { }, [&](int32_t start, int32_t end, bool) { for (int32_t i = start; i < end; i++) { - float signalTarget = ProcessSignal(mDeltaTime, mNodeInputManager.GetValueNext(2)); + float freq = mNodeInputManager.GetValueNext(2); + mRWAFreq.SetTarget(freq); + float signalTarget = ProcessSignal(mDeltaTime, mRWAFreq.Next()); *output0++ = mNodeInputManager.GetValueNext(3) * signalTarget; } } diff --git a/packages/nodegraph/tests/common/NodeGraphBatchingTest.cpp b/packages/nodegraph/tests/common/NodeGraphBatchingTest.cpp index 46edf4a7..b676245d 100644 --- a/packages/nodegraph/tests/common/NodeGraphBatchingTest.cpp +++ b/packages/nodegraph/tests/common/NodeGraphBatchingTest.cpp @@ -15,6 +15,7 @@ TEST(NodeGraphBatching, Simple) { auto nodeSine = group.NewNode(OutputType::Default); nodeSine->SetInput(1, 4.0f); // sine update rate nodeSine->SetInput(2, 1400.0f); // sine freq + nodeSine->SetInput(3, 0.5f); // sine freq auto nodeLowpass = group.NewNode(OutputType::Default); @@ -35,7 +36,7 @@ TEST(NodeGraphBatching, Simple) { auto output = &group.GetOutput(0, 8); - TEST_FUZZY(0.000151574f, output[7], 0.0001f, ""); + TEST_FUZZY(0.034771f, output[7], 0.0001f, ""); return 0; } diff --git a/packages/nodegraph/tests/common/NodeGraphSchemaTest.cpp b/packages/nodegraph/tests/common/NodeGraphSchemaTest.cpp index 1c484505..0ce45b9d 100644 --- a/packages/nodegraph/tests/common/NodeGraphSchemaTest.cpp +++ b/packages/nodegraph/tests/common/NodeGraphSchemaTest.cpp @@ -109,7 +109,7 @@ TEST(NodeGraph, FilterLowpass) { //LOG(LogInfo) << nodeLowpass.GetOutput(0); } - TEST_FUZZY(nodeLowpass.GetOutput(0), -0.005843f, 0.0001f, ""); + TEST_FUZZY(nodeLowpass.GetOutput(0), -0.201134, 0.0001f, ""); return 0; } @@ -173,8 +173,8 @@ TEST(NodeGraph, GraphGroups) { float output1 = group2.GetOutput(0); float output2 = group2.GetOutput(1); - TEST_FUZZY(output1, 0.00000537335563f, 0.00000001, ""); - TEST_FUZZY(output2, 0.00000358223701f, 0.00000001, ""); + TEST_FUZZY(output1, 0.149999559f, 0.00001, ""); + TEST_FUZZY(output2, 0.0999997109f, 0.00001, ""); return 0; } From a189da63f6d677f5fa7cf698c9367f9c35a44a69 Mon Sep 17 00:00:00 2001 From: lnd3 Date: Sun, 22 Sep 2024 23:39:31 +0200 Subject: [PATCH 31/42] Use one common node data iterator. Must distruinguish between constant and array buffers during init where set input is never used, for example in source op time. Structured definitions better. --- packages/audio/include/audio/AudioUtils.h | 4 +- packages/audio/source/common/AudioUtils.cpp | 8 +- .../include/nodegraph/core/NodeGraphBase.h | 14 +-- .../include/nodegraph/core/NodeGraphData.h | 108 ++++++++++++++++++ .../include/nodegraph/core/NodeGraphInput.h | 98 ++-------------- .../include/nodegraph/core/NodeGraphOutput.h | 34 +----- .../nodegraph/operations/NodeGraphOpControl.h | 2 + .../nodegraph/operations/NodeGraphOpNumeric.h | 30 ++--- .../nodegraph/operations/NodeGraphOpSignal.h | 4 +- .../nodegraph/operations/NodeGraphOpSource.h | 25 ++-- .../source/common/core/NodeGraphBase.cpp | 18 ++- .../source/common/core/NodeGraphData.cpp | 29 +++++ .../source/common/core/NodeGraphInput.cpp | 44 +++---- .../source/common/core/NodeGraphOutput.cpp | 25 ++-- .../common/operations/NodeGraphOpControl.cpp | 8 +- .../common/operations/NodeGraphOpEffect.cpp | 4 +- .../common/operations/NodeGraphOpOutput.cpp | 4 +- .../common/operations/NodeGraphOpSignal.cpp | 2 +- .../common/operations/NodeGraphOpSource.cpp | 12 +- .../tests/common/NodeGraphDataTest.cpp | 61 ++++++++++ 20 files changed, 316 insertions(+), 218 deletions(-) create mode 100644 packages/nodegraph/include/nodegraph/core/NodeGraphData.h create mode 100644 packages/nodegraph/source/common/core/NodeGraphData.cpp create mode 100644 packages/nodegraph/tests/common/NodeGraphDataTest.cpp diff --git a/packages/audio/include/audio/AudioUtils.h b/packages/audio/include/audio/AudioUtils.h index bcec4a50..0363d64c 100644 --- a/packages/audio/include/audio/AudioUtils.h +++ b/packages/audio/include/audio/AudioUtils.h @@ -51,8 +51,8 @@ namespace l::audio { } FilterRWA& SetConvergenceInMs(T convergenceInMS, T limit = static_cast(0.0001), T sampleRate = static_cast(44100.0)) { - mSmooth = GetRWAFactorFromMS(convergenceInMS, limit, mRWAUpdateRate, sampleRate); - mSmoothSkewed = GetRWAFactorFromMSSkewed(convergenceInMS, limit, mRWAUpdateRate, sampleRate); + mSmooth = GetRWAFactorFromMS(convergenceInMS, limit, mRWAUpdateRate, sampleRate / mRWAUpdateRate); + mSmoothSkewed = GetRWAFactorFromMSSkewed(convergenceInMS, limit, mRWAUpdateRate, sampleRate / mRWAUpdateRate); return *this; } diff --git a/packages/audio/source/common/AudioUtils.cpp b/packages/audio/source/common/AudioUtils.cpp index 647cea5d..17c5b259 100644 --- a/packages/audio/source/common/AudioUtils.cpp +++ b/packages/audio/source/common/AudioUtils.cpp @@ -29,14 +29,14 @@ namespace l::audio { return numAudioSamples * 1000.0f / sampleRate; } - float GetRWAFactorFromMS(float ms, float limit, float , float sampleRate) { - int32_t updateSteps = GetAudioTicksFromMS(ms, sampleRate); + float GetRWAFactorFromMS(float ms, float limit, float rwaUpdateRate, float sampleRate) { + int32_t updateSteps = GetAudioTicksFromMS(ms, sampleRate / rwaUpdateRate); return l::math::tween::GetRWAFactor(updateSteps, limit); } - float GetRWAFactorFromMSSkewed(float ms, float limit, float, float sampleRate) { + float GetRWAFactorFromMSSkewed(float ms, float limit, float rwaUpdateRate, float sampleRate) { float msRoot = l::math::functions::sqrt(ms); - int32_t steps = GetAudioTicksFromMS(msRoot, sampleRate); + int32_t steps = GetAudioTicksFromMS(msRoot, sampleRate / rwaUpdateRate); float factor = l::math::tween::GetRWAFactor(steps, limit); factor *= factor; return factor; diff --git a/packages/nodegraph/include/nodegraph/core/NodeGraphBase.h b/packages/nodegraph/include/nodegraph/core/NodeGraphBase.h index 9b4e7bf4..cce5d6e5 100644 --- a/packages/nodegraph/include/nodegraph/core/NodeGraphBase.h +++ b/packages/nodegraph/include/nodegraph/core/NodeGraphBase.h @@ -11,6 +11,7 @@ #include "math/MathConstants.h" +#include "nodegraph/core/NodeGraphData.h" #include "nodegraph/core/NodeGraphInput.h" #include "nodegraph/core/NodeGraphOutput.h" @@ -19,12 +20,6 @@ namespace l::nodegraph { int32_t CreateUniqueId(); bool IsValidInOutNum(int8_t inoutNum, size_t inoutSize); - enum class DataType { - FLOAT32, - INT32, - BITFIELD32 - }; - class NodeGraphGroup; @@ -32,8 +27,6 @@ namespace l::nodegraph { class NodeGraphBase { public: NodeGraphBase(OutputType outputType) : mId(CreateUniqueId()), mOutputType(outputType) { - mInputs.resize(1); - mOutputs.resize(1); } virtual ~NodeGraphBase() { LOG(LogInfo) << "Node graph base destroyed"; @@ -52,6 +45,8 @@ namespace l::nodegraph { virtual float& GetInput(int8_t inputChannel, int32_t size = 1); virtual float& GetOutput(int8_t outputChannel, int32_t size = 1); + virtual NodeGraphInput& GetInputOf(int8_t inputChannel); + virtual NodeGraphOutput& GetOutputOf(int8_t outputChannel); virtual int32_t GetInputSize(int8_t inputChannel); virtual int32_t GetOutputSize(int8_t outputChannel); @@ -66,7 +61,7 @@ namespace l::nodegraph { virtual bool SetInput(int8_t inputChannel, NodeGraphBase& source, int8_t sourceOutputChannel); virtual bool SetInput(int8_t inputChannel, NodeGraphGroup& source, int8_t sourceOutputChannel); - virtual bool SetInput(int8_t inputChannel, float constant, int32_t size = 1); + virtual bool SetInput(int8_t inputChannel, float initialValue, int32_t size = 1); virtual bool SetInput(int8_t inputChannel, float* floatPtr); virtual void SetDefaultOutput(int8_t outputChannel, float constant, int32_t size = 1); @@ -220,7 +215,6 @@ namespace l::nodegraph { T mOperation; }; - /**********************************************************************************/ class NodeInputManager { public: diff --git a/packages/nodegraph/include/nodegraph/core/NodeGraphData.h b/packages/nodegraph/include/nodegraph/core/NodeGraphData.h new file mode 100644 index 00000000..906d22b9 --- /dev/null +++ b/packages/nodegraph/include/nodegraph/core/NodeGraphData.h @@ -0,0 +1,108 @@ +#pragma once + +#include "logging/LoggingAll.h" + +#include +#include +#include +#include +#include +#include + +#include "math/MathAll.h" +#include "audio/AudioUtils.h" + +namespace l::nodegraph { + + enum class InputType { + INPUT_EMPTY, + INPUT_NODE, + INPUT_CONSTANT, + INPUT_VALUE, + INPUT_ARRAY + }; + + enum class InputBound { + INPUT_UNBOUNDED, + INPUT_0_TO_1, + INPUT_0_TO_2, + INPUT_NEG_1_POS_1, + INPUT_0_100, + INPUT_CUSTOM, + }; + + enum class OutputType { + Default, // node will be processed if it is connected to the groups output by some route + ExternalOutput, // node does not have meaningful output for other nodes but should still be processed (ex speaker output only has input) + ExternalVisualOutput, + }; + + std::pair GetInputBounds(InputBound bound); + + class NodeGraphBase; + + union Input { + NodeGraphBase* mInputNode = nullptr; + float* mInputFloat; + float mInputFloatConstant; + }; + + /**********************************************************************************/ + class NodeDataIterator { + public: + NodeDataIterator(float* data = nullptr, float stepPerIndex = 1.0f) { + mData = data; + mIncrement = stepPerIndex; + mIndex = mIncrement * 0.5f; // warmstart accumulator half a step to avoid rounding down (to previous index) close to whole integers because of floating point inprecision + } + ~NodeDataIterator() = default; + + float& operator*() { + return *(mData + static_cast(mIndex)); + } + float* operator++(int) { + auto p = mData + static_cast(mIndex); + mIndex += mIncrement; + return p; + } + float& operator[](int offset) { + return *(mData + static_cast(offset * offset)); + } + float* data() { + return mData; + } + void Reset(float* data, float stepPerIndex = 1.0f) { + mData = data; + mIncrement = stepPerIndex; + } + protected: + int32_t mSize = 0; + float mIndex = 0.0f; + float* mData = nullptr; + float mIncrement = 0.0f; + }; + + /*********************************************************************************/ + enum class InputTypeBase { + SAMPLED = 0, // todo: make it interpolate in smaller custom buffers + //SAMPLED_RWA = 0, // todo: add a smoothed sampled variant. Will replace interp_rwa and interp_rwa_ms as we will add a separate config for passing ticks or millis + INTERP_RWA, // TBR + INTERP_RWA_MS, // TBR + CONSTANT_VALUE, // Will basically be replace by sampled as it should be able to handle 1-sized arrays + CONSTANT_ARRAY, // Same here, will be replaced + CUSTOM_INTERP_TWEEN, // custom input vars should not be used at all + CUSTOM_INTERP_TWEEN_MS, + CUSTOM_INTERP_RWA_MS + }; + + union InputUnion { + l::audio::FilterRWAFloat mFilterRWA; + l::math::tween::DynamicTween mTween; + NodeDataIterator mIterator; + + InputUnion() : mFilterRWA() {} + ~InputUnion() = default; + }; + +} + diff --git a/packages/nodegraph/include/nodegraph/core/NodeGraphInput.h b/packages/nodegraph/include/nodegraph/core/NodeGraphInput.h index a6717d52..bcf19e32 100644 --- a/packages/nodegraph/include/nodegraph/core/NodeGraphInput.h +++ b/packages/nodegraph/include/nodegraph/core/NodeGraphInput.h @@ -12,73 +12,9 @@ #include "math/MathAll.h" #include "audio/AudioUtils.h" -namespace l::nodegraph { - - enum class InputType { - INPUT_EMPTY, - INPUT_NODE, - INPUT_CONSTANT, - INPUT_VALUE, - INPUT_ARRAY - }; - - enum class InputBound { - INPUT_UNBOUNDED, - INPUT_0_TO_1, - INPUT_0_TO_2, - INPUT_NEG_1_POS_1, - INPUT_0_100, - INPUT_CUSTOM, - }; - - std::pair GetInputBounds(InputBound bound); - - class NodeGraphBase; - - union Input { - NodeGraphBase* mInputNode = nullptr; - float* mInputFloat; - float mInputFloatConstant; - }; - - /*********************************************************************************/ - - class NodeInputDataIterator { - public: - NodeInputDataIterator(float* data = nullptr, int32_t size = 0) { - mData = data; - mSize = size; - mIncrement = size > 1 ? 1 : 0; - } - ~NodeInputDataIterator() = default; - - float& operator*() { - return *mData; - } - float* operator++(int) { - float* data = mData; - mData += mIncrement; - return data; - } - float operator[](int index) { - return mData[index]; - } - float* data() { - return mData; - } - void Reset(float* data, int32_t size) { - mData = data; - mSize = size; - mIncrement = size > 1 ? 1 : 0; - } - - protected: - float* mData = nullptr; - int32_t mSize = 0; - int32_t mIncrement = 0; - }; +#include "nodegraph/core/NodeGraphData.h" - /*********************************************************************************/ +namespace l::nodegraph { class NodeGraphInput { public: @@ -93,37 +29,17 @@ namespace l::nodegraph { // hack to get input buffers working std::unique_ptr> mInputBuf = nullptr; + float mInputLod = 1.0f; // buffer size level of detail value[1.0f, buffer size] (if 1 it will write all generated values to the buffer, if 'buffer size' it will only have the latest written value), void Reset(); bool HasInputNode(); - float& Get(int32_t numSamples = 1); - NodeInputDataIterator GetBufferIterator(int32_t numSamples = 1); - NodeInputDataIterator GetArrayIterator(); + float& Get(int32_t size = 1); + NodeDataIterator GetIterator(int32_t size = 1, float lod = 1.0f); + NodeDataIterator GetArrayIterator(); int32_t GetSize(); }; - /****************************************************************************************/ - enum class InputTypeBase { - SAMPLED = 0, - INTERP_RWA, - INTERP_RWA_MS, - CONSTANT_VALUE, - CONSTANT_ARRAY, - CUSTOM_INTERP_TWEEN, - CUSTOM_INTERP_TWEEN_MS, - CUSTOM_INTERP_RWA_MS - }; - - union InputUnion { - l::audio::FilterRWAFloat mFilterRWA; - l::math::tween::DynamicTween mTween; - NodeInputDataIterator mIterator; - - InputUnion() : mFilterRWA() {} - ~InputUnion() = default; - }; - /*********************************************************************************/ class NodeGraphInputAccessor { @@ -148,7 +64,7 @@ namespace l::nodegraph { case InputTypeBase::SAMPLED: case InputTypeBase::CONSTANT_VALUE: case InputTypeBase::CONSTANT_ARRAY: - new (&mInput.mIterator) NodeInputDataIterator(); + new (&mInput.mIterator) NodeDataIterator(nullptr); //mInput.mIterator = NodeInputDataIterator(); break; } diff --git a/packages/nodegraph/include/nodegraph/core/NodeGraphOutput.h b/packages/nodegraph/include/nodegraph/core/NodeGraphOutput.h index 0ab413a7..4d31659d 100644 --- a/packages/nodegraph/include/nodegraph/core/NodeGraphOutput.h +++ b/packages/nodegraph/include/nodegraph/core/NodeGraphOutput.h @@ -11,47 +11,23 @@ #include "math/MathConstants.h" -namespace l::nodegraph { - - enum class OutputType { - Default, // node will be processed if it is connected to the groups output by some route - ExternalOutput, // node does not have meaningful output for other nodes but should still be processed (ex speaker output only has input) - ExternalVisualOutput, - }; - +#include "nodegraph/core/NodeGraphData.h" - class NodeOutputDataIterator { - public: - NodeOutputDataIterator(float* data, int32_t size) { - mData = data; - mSize = size; - mIncrement = size > 1 ? 1 : 0; - } - float& operator*() { - return *mData; - } - float* operator++(int) { - float* data = mData; - mData += mIncrement; - return data; - } - protected: - float* mData = nullptr; - int32_t mSize = 0; - int32_t mIncrement = 0; - }; +namespace l::nodegraph { class NodeGraphOutput { public: NodeGraphOutput() = default; float mOutput = 0.0f; + float mOutputLod = 1.0f; // buffer size level of detail value[1.0f, buffer size] (if 1 it will write all generated values to the buffer, if 'buffer size' it will only have the latest written value), std::unique_ptr> mOutputBuf = nullptr; std::unique_ptr mName = nullptr; bool mOutputPolled = false; float& Get(int32_t size = 1); - NodeOutputDataIterator GetIterator(int32_t numSamples = 1); + NodeDataIterator GetIterator(int32_t size, float lod = 1.0f); + NodeDataIterator GetIterator(); int32_t GetSize(); bool IsPolled(); void ResetPollState(); diff --git a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpControl.h b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpControl.h index 64772469..eb2bc1b1 100644 --- a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpControl.h +++ b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpControl.h @@ -91,6 +91,8 @@ namespace l::nodegraph { class GraphControlArpeggio: public NodeGraphOp { public: + const float gArpeggioUpdateRate = 16.0f; + const static int32_t gPolyphony = 12; GraphControlArpeggio(NodeGraphBase* node) : NodeGraphOp(node, "Arpeggio"), diff --git a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpNumeric.h b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpNumeric.h index 012d3555..41cd599a 100644 --- a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpNumeric.h +++ b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpNumeric.h @@ -34,8 +34,8 @@ namespace l::nodegraph { } virtual ~GraphNumericAdd() = default; virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override { - auto input0 = inputs.at(0).GetBufferIterator(numSamples); - auto input1 = inputs.at(1).GetBufferIterator(numSamples); + auto input0 = inputs.at(0).GetIterator(numSamples); + auto input1 = inputs.at(1).GetIterator(numSamples); auto output = outputs.at(0).GetIterator(numSamples); if (numSamples > 1) { @@ -61,8 +61,8 @@ namespace l::nodegraph { virtual ~GraphNumericMultiply() = default; virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override { - auto input0 = inputs.at(0).GetBufferIterator(numSamples); - auto input1 = inputs.at(1).GetBufferIterator(numSamples); + auto input0 = inputs.at(0).GetIterator(numSamples); + auto input1 = inputs.at(1).GetIterator(numSamples); auto output = outputs.at(0).GetIterator(numSamples); if (numSamples > 1) { @@ -87,8 +87,8 @@ namespace l::nodegraph { } virtual ~GraphNumericSubtract() = default; virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override { - auto input0 = inputs.at(0).GetBufferIterator(numSamples); - auto input1 = inputs.at(1).GetBufferIterator(numSamples); + auto input0 = inputs.at(0).GetIterator(numSamples); + auto input1 = inputs.at(1).GetIterator(numSamples); auto output = outputs.at(0).GetIterator(numSamples); if (numSamples > 1) { @@ -113,7 +113,7 @@ namespace l::nodegraph { virtual ~GraphNumericNegate() = default; virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override { - auto input0 = inputs.at(0).GetBufferIterator(numSamples); + auto input0 = inputs.at(0).GetIterator(numSamples); auto output = outputs.at(0).GetIterator(numSamples); if (numSamples > 1) { @@ -138,7 +138,7 @@ namespace l::nodegraph { virtual ~GraphNumericIntegral() = default; virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override { - auto input0 = inputs.at(0).GetBufferIterator(numSamples); + auto input0 = inputs.at(0).GetIterator(numSamples); auto output = outputs.at(0).GetIterator(numSamples); if (numSamples > 1) { @@ -168,9 +168,9 @@ namespace l::nodegraph { virtual ~GraphNumericMultiply3() = default; virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override { - auto input0 = inputs.at(0).GetBufferIterator(numSamples); - auto input1 = inputs.at(1).GetBufferIterator(numSamples); - auto input2 = inputs.at(2).GetBufferIterator(numSamples); + auto input0 = inputs.at(0).GetIterator(numSamples); + auto input1 = inputs.at(1).GetIterator(numSamples); + auto input2 = inputs.at(2).GetIterator(numSamples); auto output = outputs.at(0).GetIterator(numSamples); if (numSamples > 1) { @@ -197,9 +197,9 @@ namespace l::nodegraph { virtual ~GraphNumericMultiplyAndAdd() = default; void virtual Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override { - auto input0 = inputs.at(0).GetBufferIterator(numSamples); - auto input1 = inputs.at(1).GetBufferIterator(numSamples); - auto input2 = inputs.at(2).GetBufferIterator(numSamples); + auto input0 = inputs.at(0).GetIterator(numSamples); + auto input1 = inputs.at(1).GetIterator(numSamples); + auto input2 = inputs.at(2).GetIterator(numSamples); auto output = outputs.at(0).GetIterator(numSamples); if (numSamples > 1) { @@ -226,7 +226,7 @@ namespace l::nodegraph { virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override { outputs.at(0).mOutput = l::math::functions::round(inputs.at(0).Get()); - auto input0 = inputs.at(2).GetBufferIterator(numSamples); + auto input0 = inputs.at(2).GetIterator(numSamples); auto output = outputs.at(0).GetIterator(numSamples); if (numSamples > 1) { diff --git a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpSignal.h b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpSignal.h index 079e0179..1413e867 100644 --- a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpSignal.h +++ b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpSignal.h @@ -76,8 +76,8 @@ namespace l::nodegraph { GraphSignalSine2(NodeGraphBase* node) : GraphSignalBase(node, "Sine 2") { - mNodeInputManager.AddInputBase(InputTypeBase::INTERP_RWA, AddInput("Fmod", 0.0f, 1, 0.0f, 1.0f)); - mNodeInputManager.AddInputBase(InputTypeBase::INTERP_RWA, AddInput("Phase", 0.0f, 1, 0.0f, 1.0f)); + mNodeInputManager.AddInputBase(InputTypeBase::CONSTANT_VALUE, AddInput("Fmod", 0.0f, 1, 0.0f, 1.0f)); + mNodeInputManager.AddInputBase(InputTypeBase::CONSTANT_VALUE, AddInput("Phase", 0.0f, 1, 0.0f, 1.0f)); } virtual ~GraphSignalSine2() = default; diff --git a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpSource.h b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpSource.h index 9efa0ab5..c79e81f1 100644 --- a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpSource.h +++ b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpSource.h @@ -47,7 +47,9 @@ namespace l::nodegraph { AddConstant("Out 4", 0.0f, 1, bound.first, bound.second); } - virtual ~GraphSourceConstants() = default; + virtual ~GraphSourceConstants() { + LOG(LogInfo) << "sdad"; + } virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; virtual void Tick(int32_t, float) override; protected: @@ -64,15 +66,15 @@ namespace l::nodegraph { mAudioRate(audioRate), mFrameRate(frameRate) { - AddOutput("Audio Tick"); - AddOutput("Frame Tick"); - AddOutput("Audio Time"); - AddOutput("Frame Time"); + AddOutput("Audio Tick", 0.0f, 2); + AddOutput("Frame Tick", 0.0f, 2); + AddOutput("Audio Time", 0.0f, 2); + AddOutput("Frame Time", 0.0f, 2); - AddConstant("Audio Tick", 0.0f); - AddConstant("Frame Tick", 0.0f); - AddConstant("Audio Time", 0.0f); - AddConstant("Frame Time", 0.0f); + AddConstant("Audio Tick", 0.0f, 2); + AddConstant("Frame Tick", 0.0f, 2); + AddConstant("Audio Time", 0.0f, 2); + AddConstant("Frame Time", 0.0f, 2); AddInput("Reset", 0.0f, 1, 0.0f, 1.0f); @@ -82,7 +84,10 @@ namespace l::nodegraph { mAudioTick = 0; } - virtual ~GraphSourceTime() = default; + virtual ~GraphSourceTime() { + LOG(LogInfo) << "sdad"; + } + virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; virtual void Tick(int32_t, float) override; protected: diff --git a/packages/nodegraph/source/common/core/NodeGraphBase.cpp b/packages/nodegraph/source/common/core/NodeGraphBase.cpp index 33c0d158..cf9827ab 100644 --- a/packages/nodegraph/source/common/core/NodeGraphBase.cpp +++ b/packages/nodegraph/source/common/core/NodeGraphBase.cpp @@ -90,6 +90,14 @@ namespace l::nodegraph { return mOutputs.at(outputChannel).Get(size); } + NodeGraphInput& NodeGraphBase::GetInputOf(int8_t inputChannel) { + return mInputs.at(inputChannel); + } + + NodeGraphOutput& NodeGraphBase::GetOutputOf(int8_t outputChannel) { + return mOutputs.at(outputChannel); + } + int32_t NodeGraphBase::GetInputSize(int8_t inputChannel) { return mInputs.at(inputChannel).GetSize(); } @@ -165,15 +173,15 @@ namespace l::nodegraph { return true; } - bool NodeGraphBase::SetInput(int8_t inputChannel, float constant, int32_t size) { + bool NodeGraphBase::SetInput(int8_t inputChannel, float initialValue, int32_t size) { ASSERT(inputChannel >= 0 && static_cast(inputChannel) < mInputs.size()); if (!IsValidInOutNum(inputChannel, mInputs.size())) { return false; } auto& input = mInputs.at(inputChannel); - constant = l::math::functions::clamp(constant, input.mBoundMin, input.mBoundMax); + initialValue = l::math::functions::clamp(initialValue, input.mBoundMin, input.mBoundMax); if (size <= 1) { - input.mInput.mInputFloatConstant = constant; + input.mInput.mInputFloatConstant = initialValue; input.mInputType = InputType::INPUT_CONSTANT; input.mInputFromOutputChannel = 0; } @@ -182,9 +190,9 @@ namespace l::nodegraph { input.mInputFromOutputChannel = 0; auto inputBuf = &input.Get(size); for (int32_t i = 0; i < size; i++) { - *inputBuf++ = constant; + *inputBuf++ = initialValue; } - input.mInput.mInputFloatConstant = constant; + input.mInput.mInputFloatConstant = initialValue; } return true; diff --git a/packages/nodegraph/source/common/core/NodeGraphData.cpp b/packages/nodegraph/source/common/core/NodeGraphData.cpp new file mode 100644 index 00000000..17558e3f --- /dev/null +++ b/packages/nodegraph/source/common/core/NodeGraphData.cpp @@ -0,0 +1,29 @@ +#include "nodegraph/core/NodeGraphData.h" + +#include "logging/Log.h" + +#include "math/MathFunc.h" + +namespace l::nodegraph { + + std::pair GetInputBounds(InputBound bound) { + switch (bound) { + case InputBound::INPUT_0_TO_1: + return { 0.0f, 1.0f }; + case InputBound::INPUT_0_TO_2: + return { 0.0f, 2.0f }; + case InputBound::INPUT_NEG_1_POS_1: + return { -1.0f, 1.0f }; + case InputBound::INPUT_0_100: + return { 0.0f, 100.0f }; + case InputBound::INPUT_UNBOUNDED: + return { -l::math::constants::FLTMAX, l::math::constants::FLTMAX }; + case InputBound::INPUT_CUSTOM: + return { 0.0f, 0.0f }; + } + return { 0.0f, 0.0f }; + } + + /*********************************************************************************/ + +} \ No newline at end of file diff --git a/packages/nodegraph/source/common/core/NodeGraphInput.cpp b/packages/nodegraph/source/common/core/NodeGraphInput.cpp index 682d3e7b..b0d7cfd7 100644 --- a/packages/nodegraph/source/common/core/NodeGraphInput.cpp +++ b/packages/nodegraph/source/common/core/NodeGraphInput.cpp @@ -7,26 +7,6 @@ namespace l::nodegraph { - std::pair GetInputBounds(InputBound bound) { - switch (bound) { - case InputBound::INPUT_0_TO_1: - return { 0.0f, 1.0f }; - case InputBound::INPUT_0_TO_2: - return { 0.0f, 2.0f }; - case InputBound::INPUT_NEG_1_POS_1: - return { -1.0f, 1.0f }; - case InputBound::INPUT_0_100: - return { 0.0f, 100.0f }; - case InputBound::INPUT_UNBOUNDED: - return { -l::math::constants::FLTMAX, l::math::constants::FLTMAX }; - case InputBound::INPUT_CUSTOM: - return { 0.0f, 0.0f }; - } - return { 0.0f, 0.0f }; - } - - /*********************************************************************************/ - void NodeGraphInput::Reset() { if (mInputType == InputType::INPUT_NODE || mInputType == InputType::INPUT_VALUE) { mInput.mInputNode = nullptr; @@ -70,17 +50,25 @@ namespace l::nodegraph { return mInput.mInputFloatConstant; } - NodeInputDataIterator NodeGraphInput::GetBufferIterator(int32_t numSamples) { - auto size = GetSize(); - if (size > 1) { - ASSERT(size == numSamples); + NodeDataIterator NodeGraphInput::GetIterator(int32_t size, float lod) { + if (mInputType == InputType::INPUT_NODE && mInput.mInputNode != nullptr) { + return mInput.mInputNode->GetOutputOf(mInputFromOutputChannel).GetIterator(); + } + else if (mInputType == InputType::INPUT_CONSTANT) { + return NodeDataIterator(&Get(1), 0.0f); + } + else { + if (mInputLod == 1.0f && lod > 1.0f) { + mInputLod = lod; + } + float stepPerIndex = size == 1 ? 0.0f : 1.0f / mInputLod; + return NodeDataIterator(&Get(size), stepPerIndex); } - return NodeInputDataIterator(&Get(size), size); } - NodeInputDataIterator NodeGraphInput::GetArrayIterator() { + NodeDataIterator NodeGraphInput::GetArrayIterator() { auto size = GetSize(); - return NodeInputDataIterator(&Get(size), size); + return NodeDataIterator(&Get(size)); } int32_t NodeGraphInput::GetSize() { @@ -246,7 +234,7 @@ namespace l::nodegraph { case InputTypeBase::CUSTOM_INTERP_TWEEN_MS: break; case InputTypeBase::SAMPLED: - mInput.mIterator = input.at(mInputIndex).GetBufferIterator(numSamples); + mInput.mIterator = input.at(mInputIndex).GetIterator(numSamples); break; case InputTypeBase::CONSTANT_VALUE: mInput.mIterator.Reset(&input.at(mInputIndex).Get(), 1); diff --git a/packages/nodegraph/source/common/core/NodeGraphOutput.cpp b/packages/nodegraph/source/common/core/NodeGraphOutput.cpp index 04c2e77f..27762df8 100644 --- a/packages/nodegraph/source/common/core/NodeGraphOutput.cpp +++ b/packages/nodegraph/source/common/core/NodeGraphOutput.cpp @@ -1,4 +1,5 @@ #include "nodegraph/core/NodeGraphOutput.h" +#include "nodegraph/core/NodeGraphBase.h" #include "logging/Log.h" @@ -15,8 +16,12 @@ namespace l::nodegraph { mOutputBuf = std::make_unique>(); } } - if (static_cast(mOutputBuf->size()) < size) { - mOutputBuf->resize(size); + int32_t lodSize = static_cast(size / mOutputLod); + if (lodSize != size) { + ASSERT(lodSize != size) << "Failed to reset 'lod' output buffer to size '" << lodSize << "' because it is already allocated for size '" << mOutputBuf->size() << "'"; + } + if (static_cast(mOutputBuf->size()) < lodSize) { + mOutputBuf->resize(lodSize); for (size_t i = 0; i < mOutputBuf->size(); i++) { (*mOutputBuf)[i] = 0.0f; } @@ -25,12 +30,18 @@ namespace l::nodegraph { return *mOutputBuf->data(); } - NodeOutputDataIterator NodeGraphOutput::GetIterator(int32_t numSamples) { - auto size = GetSize(); - if (size > 1) { - ASSERT(size == numSamples); + NodeDataIterator NodeGraphOutput::GetIterator(int32_t size, float lod) { + if (mOutputLod == 1.0f && lod > 1.0f) { + mOutputLod = lod; } - return NodeOutputDataIterator(&Get(numSamples), size); + float stepPerIndex = size == 1 ? 0.0f : 1.0f / mOutputLod; + return NodeDataIterator(&Get(size), stepPerIndex); + } + + NodeDataIterator NodeGraphOutput::GetIterator() { + auto size = GetSize(); + float stepPerIndex = size == 1 ? 0.0f : 1.0f / mOutputLod; + return NodeDataIterator(&Get(size), stepPerIndex); } int32_t NodeGraphOutput::GetSize() { diff --git a/packages/nodegraph/source/common/operations/NodeGraphOpControl.cpp b/packages/nodegraph/source/common/operations/NodeGraphOpControl.cpp index bea78c81..db1c952a 100644 --- a/packages/nodegraph/source/common/operations/NodeGraphOpControl.cpp +++ b/packages/nodegraph/source/common/operations/NodeGraphOpControl.cpp @@ -122,8 +122,8 @@ namespace l::nodegraph { void GraphControlArpeggio::UpdateSignal(NodeInputManager& inputManager) { float attack = inputManager.GetValue(4); float release = inputManager.GetValue(5); - float attackFactor = l::audio::GetRWAFactorFromMS(attack, 0.001f); - float releaseFactor = l::audio::GetRWAFactorFromMS(release, 0.001f); + float attackFactor = l::audio::GetRWAFactorFromMS(attack, 0.001f, gArpeggioUpdateRate); + float releaseFactor = l::audio::GetRWAFactorFromMS(release, 0.001f, gArpeggioUpdateRate); mGainAttack = attackFactor; mGainRelease = releaseFactor; @@ -192,8 +192,8 @@ namespace l::nodegraph { } } - auto output0 = outputs.at(0).GetIterator(numSamples); - auto output1 = outputs.at(1).GetIterator(numSamples); + auto output0 = outputs.at(0).GetIterator(numSamples, gArpeggioUpdateRate); + auto output1 = outputs.at(1).GetIterator(numSamples, gArpeggioUpdateRate); mSamplesUntilUpdate = l::audio::BatchUpdate(mUpdateRate, mSamplesUntilUpdate, 0, numSamples, [&]() { mNodeInputManager.NodeUpdate(inputs, mUpdateRate); diff --git a/packages/nodegraph/source/common/operations/NodeGraphOpEffect.cpp b/packages/nodegraph/source/common/operations/NodeGraphOpEffect.cpp index 78679507..4c25d152 100644 --- a/packages/nodegraph/source/common/operations/NodeGraphOpEffect.cpp +++ b/packages/nodegraph/source/common/operations/NodeGraphOpEffect.cpp @@ -17,8 +17,8 @@ namespace l::nodegraph { mSamplesUntilUpdate = 0; } - auto input0 = inputs.at(4).GetBufferIterator(numSamples); - auto input1 = inputs.at(5).GetBufferIterator(numSamples); + auto input0 = inputs.at(4).GetIterator(numSamples); + auto input1 = inputs.at(5).GetIterator(numSamples); auto output0 = outputs.at(0).GetIterator(numSamples); auto output1 = outputs.at(1).GetIterator(numSamples); diff --git a/packages/nodegraph/source/common/operations/NodeGraphOpOutput.cpp b/packages/nodegraph/source/common/operations/NodeGraphOpOutput.cpp index 5fc9ecad..3e93f740 100644 --- a/packages/nodegraph/source/common/operations/NodeGraphOpOutput.cpp +++ b/packages/nodegraph/source/common/operations/NodeGraphOpOutput.cpp @@ -24,8 +24,8 @@ namespace l::nodegraph { } auto& buffer = mAudioStream->GetWriteBuffer(); - auto in0 = inputs.at(0).GetBufferIterator(numSamples); - auto in1 = inputs.at(1).GetBufferIterator(numSamples); + auto in0 = inputs.at(0).GetIterator(numSamples); + auto in1 = inputs.at(1).GetIterator(numSamples); float updateRate = 256.0f; mSamplesUntilUpdate = l::audio::BatchUpdate(updateRate, mSamplesUntilUpdate, 0, numSamples, diff --git a/packages/nodegraph/source/common/operations/NodeGraphOpSignal.cpp b/packages/nodegraph/source/common/operations/NodeGraphOpSignal.cpp index 5063f397..65f5dcec 100644 --- a/packages/nodegraph/source/common/operations/NodeGraphOpSignal.cpp +++ b/packages/nodegraph/source/common/operations/NodeGraphOpSignal.cpp @@ -39,7 +39,7 @@ namespace l::nodegraph { for (int32_t i = start; i < end; i++) { float freq = mNodeInputManager.GetValueNext(2); mRWAFreq.SetTarget(freq); - float signalTarget = ProcessSignal(mDeltaTime, mRWAFreq.Next()); + float signalTarget = ProcessSignal(mDeltaTime, freq); *output0++ = mNodeInputManager.GetValueNext(3) * signalTarget; } } diff --git a/packages/nodegraph/source/common/operations/NodeGraphOpSource.cpp b/packages/nodegraph/source/common/operations/NodeGraphOpSource.cpp index 28a82e50..5042822f 100644 --- a/packages/nodegraph/source/common/operations/NodeGraphOpSource.cpp +++ b/packages/nodegraph/source/common/operations/NodeGraphOpSource.cpp @@ -13,7 +13,7 @@ namespace l::nodegraph { /*********************************************************************/ void GraphSourceConstants::Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) { - auto input0 = inputs.at(0).GetBufferIterator(numSamples); + auto input0 = inputs.at(0).GetIterator(numSamples); auto output0 = outputs.at(0).GetIterator(numSamples); for (int8_t i = 0; i < mNumOutputs; i++) { @@ -28,14 +28,14 @@ namespace l::nodegraph { /*********************************************************************/ void GraphSourceTime::Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) { - auto input0 = inputs.at(0).GetBufferIterator(numSamples); - auto input1 = inputs.at(1).GetBufferIterator(numSamples); - auto input2 = inputs.at(2).GetBufferIterator(numSamples); - auto input3 = inputs.at(3).GetBufferIterator(numSamples); + auto input0 = inputs.at(0).GetIterator(numSamples); + auto input1 = inputs.at(1).GetIterator(numSamples); + auto input2 = inputs.at(2).GetIterator(numSamples); + auto input3 = inputs.at(3).GetIterator(numSamples); auto output0 = outputs.at(0).GetIterator(numSamples); auto output1 = outputs.at(1).GetIterator(numSamples); auto output2 = outputs.at(2).GetIterator(numSamples); - auto output3 = outputs.at(03).GetIterator(numSamples); + auto output3 = outputs.at(3).GetIterator(numSamples); if (inputs.at(4).Get() > 0.5f) { mAudioTick = 0; diff --git a/packages/nodegraph/tests/common/NodeGraphDataTest.cpp b/packages/nodegraph/tests/common/NodeGraphDataTest.cpp new file mode 100644 index 00000000..917cb66f --- /dev/null +++ b/packages/nodegraph/tests/common/NodeGraphDataTest.cpp @@ -0,0 +1,61 @@ +#include "testing/Test.h" +#include "logging/Log.h" + +#include "nodegraph/core/NodeGraphData.h" +#include "nodegraph/core/NodeGraphInput.h" +#include "nodegraph/core/NodeGraphOutput.h" +#include "nodegraph/core/NodeGraphBase.h" + +using namespace l; +using namespace l::nodegraph; + + +class TestOp : public NodeGraphOp { +public: + TestOp(NodeGraphBase* node) : + NodeGraphOp(node, "TestOp"), + mNodeInputManager(*this) + { + mNodeInputManager.AddInputBase(InputTypeBase::SAMPLED, AddInput("In", 0.0f, 1, 0.0f, 1.0f)); + AddOutput("Out"); + } + + virtual ~TestOp() = default; + virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override { + mNodeInputManager.ProcessUpdate(inputs, numSamples, 4.0f); + auto in = inputs.at(0).GetIterator(numSamples); + auto out = outputs.at(0).GetIterator(numSamples, 4.0f); + + for (int i = 0; i < numSamples; i++) { + *out++ = *in++; + } + } +protected: + NodeInputManager mNodeInputManager; +}; + + + +TEST(NodeGraphData, Setup) { + + NodeGraph node0; + NodeGraph node1; + + auto input = node0.GetInputOf(0).GetIterator(16); + for (int i = 0; i < 16; i++) { + *input++ = i; + } + + node1.SetInput(0, node0, 0); + node1.ProcessSubGraph(16); + + auto output = node1.GetOutputOf(0).GetIterator(); + + for (int i = 0; i < 16; i++) { + LOG(LogInfo) << "Output(" << i << "): " << *output++; + } + + return 0; +} + + From 5e4c970da63df0f1937d36787dcc84a65d271ac4 Mon Sep 17 00:00:00 2001 From: lnd3 Date: Tue, 24 Sep 2024 01:00:34 +0200 Subject: [PATCH 32/42] Split custom and input vars into separate id ranges. Add rwa interpolated output buffers. Set rwa factor independently to enable parameters to be updated and interpolated at different rates depending on where it is used. Fix errors here and there. --- packages/audio/include/audio/AudioUtils.h | 4 +- .../include/nodegraph/core/NodeGraphBase.h | 52 ++++++++- .../include/nodegraph/core/NodeGraphData.h | 35 +++++- .../include/nodegraph/core/NodeGraphInput.h | 7 +- .../nodegraph/operations/NodeGraphOpControl.h | 43 ++++--- .../nodegraph/operations/NodeGraphOpFilter.h | 8 +- .../nodegraph/operations/NodeGraphOpOutput.h | 2 +- .../nodegraph/operations/NodeGraphOpSignal.h | 18 +-- .../source/common/core/NodeGraphBase.cpp | 106 ++++++++++++++--- .../source/common/core/NodeGraphGroup.cpp | 4 +- .../source/common/core/NodeGraphInput.cpp | 77 +++++++++---- .../common/operations/NodeGraphOpControl.cpp | 109 ++++++++++-------- .../common/operations/NodeGraphOpFilter.cpp | 2 +- .../common/operations/NodeGraphOpOutput.cpp | 2 +- .../common/operations/NodeGraphOpSignal.cpp | 4 +- .../tests/common/NodeGraphDataTest.cpp | 47 ++++---- 16 files changed, 360 insertions(+), 160 deletions(-) diff --git a/packages/audio/include/audio/AudioUtils.h b/packages/audio/include/audio/AudioUtils.h index 0363d64c..bcec4a50 100644 --- a/packages/audio/include/audio/AudioUtils.h +++ b/packages/audio/include/audio/AudioUtils.h @@ -51,8 +51,8 @@ namespace l::audio { } FilterRWA& SetConvergenceInMs(T convergenceInMS, T limit = static_cast(0.0001), T sampleRate = static_cast(44100.0)) { - mSmooth = GetRWAFactorFromMS(convergenceInMS, limit, mRWAUpdateRate, sampleRate / mRWAUpdateRate); - mSmoothSkewed = GetRWAFactorFromMSSkewed(convergenceInMS, limit, mRWAUpdateRate, sampleRate / mRWAUpdateRate); + mSmooth = GetRWAFactorFromMS(convergenceInMS, limit, mRWAUpdateRate, sampleRate); + mSmoothSkewed = GetRWAFactorFromMSSkewed(convergenceInMS, limit, mRWAUpdateRate, sampleRate); return *this; } diff --git a/packages/nodegraph/include/nodegraph/core/NodeGraphBase.h b/packages/nodegraph/include/nodegraph/core/NodeGraphBase.h index cce5d6e5..a05bf208 100644 --- a/packages/nodegraph/include/nodegraph/core/NodeGraphBase.h +++ b/packages/nodegraph/include/nodegraph/core/NodeGraphBase.h @@ -218,19 +218,23 @@ namespace l::nodegraph { class NodeInputManager { public: + const int32_t gCustomIndexBase = 100; + NodeInputManager(NodeGraphOp& nodeGraphOperation) : mNodeGraphOperation(nodeGraphOperation) { } ~NodeInputManager() = default; - int32_t AddInputBase(InputTypeBase type, int32_t inputIndex = -1); - void ProcessUpdate(std::vector& inputs, int32_t numSamples, float updateRate); + int32_t AddInput(InputTypeBase type, int32_t inputIndex = -1); + int32_t AddCustom(InputTypeBase type); + void BatchUpdate(std::vector& inputs, int32_t numSamples); void NodeUpdate(std::vector& inputs, float updateRate); float GetValueNext(int32_t inputIndex); float GetValue(int32_t inputIndex); float GetArrayValue(int32_t inputIndex, int32_t arrayIndex); float* GetArray(int32_t inputIndex); + void SetUpdateRate(int32_t inputIndex, float updateRate); void SetDuration(int32_t inputIndex, float value, float limit = 0.001f); void SetTarget(int32_t inputIndex, float value); void SetValue(int32_t inputIndex, float value); @@ -238,7 +242,49 @@ namespace l::nodegraph { protected: NodeGraphOp& mNodeGraphOperation; std::vector mInputs; - //std::vector mInputsRefs; + std::vector mCustom; + }; + + class NodeGraphOp2 : public NodeGraphOp { + public: + NodeGraphOp2(NodeGraphBase* node, std::string_view name) : + NodeGraphOp(node, name), + mNodeInputManager(*this) { + + } + ~NodeGraphOp2() { + + } + + virtual void Tick(int32_t, float) override; + + int32_t AddInput( + InputTypeBase type, + std::string_view name, + float defaultValue = 0.0f, + int32_t size = 1, + float boundMin = -l::math::constants::FLTMAX, + float boundMax = l::math::constants::FLTMAX, + bool visible = true, + bool editable = true); + + int32_t AddConstant( + InputTypeBase type, + std::string_view name, + float defaultValue = 0.0f, + int32_t size = 1, + float boundMin = -l::math::constants::FLTMAX, + float boundMax = l::math::constants::FLTMAX, + bool visible = true, + bool editable = true); + + int32_t AddCustom( + InputTypeBase type); + + protected: + NodeInputManager mNodeInputManager; + float mStepsUntilUpdate = 0.0f; + float mUpdateRate = 16.0f; }; } diff --git a/packages/nodegraph/include/nodegraph/core/NodeGraphData.h b/packages/nodegraph/include/nodegraph/core/NodeGraphData.h index 906d22b9..704d4ae8 100644 --- a/packages/nodegraph/include/nodegraph/core/NodeGraphData.h +++ b/packages/nodegraph/include/nodegraph/core/NodeGraphData.h @@ -15,7 +15,6 @@ namespace l::nodegraph { enum class InputType { - INPUT_EMPTY, INPUT_NODE, INPUT_CONSTANT, INPUT_VALUE, @@ -50,9 +49,9 @@ namespace l::nodegraph { /**********************************************************************************/ class NodeDataIterator { public: - NodeDataIterator(float* data = nullptr, float stepPerIndex = 1.0f) { + NodeDataIterator(float* data = nullptr, float increment = 1.0f) { mData = data; - mIncrement = stepPerIndex; + mIncrement = increment; mIndex = mIncrement * 0.5f; // warmstart accumulator half a step to avoid rounding down (to previous index) close to whole integers because of floating point inprecision } ~NodeDataIterator() = default; @@ -66,7 +65,7 @@ namespace l::nodegraph { return p; } float& operator[](int offset) { - return *(mData + static_cast(offset * offset)); + return *(mData + static_cast(offset * mIncrement)); } float* data() { return mData; @@ -75,6 +74,10 @@ namespace l::nodegraph { mData = data; mIncrement = stepPerIndex; } + + float GetStepsPerIncrement() { + return 1.0f / mIncrement; + } protected: int32_t mSize = 0; float mIndex = 0.0f; @@ -82,10 +85,31 @@ namespace l::nodegraph { float mIncrement = 0.0f; }; + class NodeDataIteratorRwa { + public: + NodeDataIteratorRwa(NodeDataIterator&& iterator) { + Reset(std::move(iterator)); + } + float operator*() { + return mRwa.Value(); + } + float operator++(int) { + mRwa.SetTarget(*mIterator++); + return mRwa.Next(); + } + void Reset(NodeDataIterator&& iterator) { + mIterator = std::move(iterator); + mRwa.Value() = *mIterator; + mRwa.SetConvergenceInTicks(iterator.GetStepsPerIncrement(), 0.35f); + } + protected: + NodeDataIterator mIterator; + l::audio::FilterRWAFloat mRwa; + }; /*********************************************************************************/ enum class InputTypeBase { SAMPLED = 0, // todo: make it interpolate in smaller custom buffers - //SAMPLED_RWA = 0, // todo: add a smoothed sampled variant. Will replace interp_rwa and interp_rwa_ms as we will add a separate config for passing ticks or millis + SAMPLED_RWA, // todo: add a smoothed sampled variant. Will replace interp_rwa and interp_rwa_ms as we will add a separate config for passing ticks or millis INTERP_RWA, // TBR INTERP_RWA_MS, // TBR CONSTANT_VALUE, // Will basically be replace by sampled as it should be able to handle 1-sized arrays @@ -99,6 +123,7 @@ namespace l::nodegraph { l::audio::FilterRWAFloat mFilterRWA; l::math::tween::DynamicTween mTween; NodeDataIterator mIterator; + NodeDataIteratorRwa mIteratorRwa; InputUnion() : mFilterRWA() {} ~InputUnion() = default; diff --git a/packages/nodegraph/include/nodegraph/core/NodeGraphInput.h b/packages/nodegraph/include/nodegraph/core/NodeGraphInput.h index bcf19e32..6b04a6a6 100644 --- a/packages/nodegraph/include/nodegraph/core/NodeGraphInput.h +++ b/packages/nodegraph/include/nodegraph/core/NodeGraphInput.h @@ -19,7 +19,7 @@ namespace l::nodegraph { class NodeGraphInput { public: Input mInput; - InputType mInputType = InputType::INPUT_EMPTY; + InputType mInputType = InputType::INPUT_CONSTANT; float mBoundMin = -l::math::constants::FLTMAX; float mBoundMax = l::math::constants::FLTMAX; @@ -34,7 +34,7 @@ namespace l::nodegraph { void Reset(); bool HasInputNode(); float& Get(int32_t size = 1); - NodeDataIterator GetIterator(int32_t size = 1, float lod = 1.0f); + NodeDataIterator GetIterator(int32_t size = 1); NodeDataIterator GetArrayIterator(); int32_t GetSize(); }; @@ -71,6 +71,7 @@ namespace l::nodegraph { } ~NodeGraphInputAccessor() = default; + void SetUpdateRate(float updateRate); void SetDuration(float ms, float limit = 0.001f); void SetDuration(int32_t ticks); void SetTarget(float value); @@ -79,7 +80,7 @@ namespace l::nodegraph { float GetValue(); float GetArrayValue(int32_t index); float* GetArray(); - void ProcessUpdate(std::vector& input, int32_t numSamples, float updateRate); + void BatchUpdate(std::vector& input, int32_t numSamples); void NodeUpdate(std::vector& input, float updateRate); protected: diff --git a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpControl.h b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpControl.h index eb2bc1b1..9ad3cbd8 100644 --- a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpControl.h +++ b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpControl.h @@ -25,19 +25,21 @@ namespace l::nodegraph { class GraphControlBase : public NodeGraphOp { public: - static const int8_t mNumDefaultInputs = 7; + static const int8_t mNumDefaultInputs = 6; static const int8_t mNumDefaultOutputs = 2; GraphControlBase(NodeGraphBase* node, std::string_view name) : NodeGraphOp(node, name), mNodeInputManager(*this) { - mNodeInputManager.AddInputBase(InputTypeBase::CONSTANT_VALUE, AddInput("Sync", 0.0f, 1, 0.0f, 1.0f)); - mNodeInputManager.AddInputBase(InputTypeBase::CONSTANT_VALUE, AddInput("Rate", 256.0f, 1, 1.0f, 2048.0f)); - mNodeInputManager.AddInputBase(InputTypeBase::CONSTANT_VALUE, AddInput("Velocity", 0.5f, 1, 0.0f, 1.0f)); - mNodeInputManager.AddInputBase(InputTypeBase::CONSTANT_VALUE, AddInput("Fade", 0.1f, 1, 0.0001f, 1.0f)); - mNodeInputManager.AddInputBase(InputTypeBase::CONSTANT_VALUE, AddInput("Attack", 50.0f, 1, 1.0f, 10000.0f)); - mNodeInputManager.AddInputBase(InputTypeBase::CONSTANT_VALUE, AddInput("Release", 50.0f, 1, 1.0f, 10000.0f)); + mNodeInputManager.AddInput(InputTypeBase::CONSTANT_VALUE, AddInput("Sync", 0.0f, 1, 0.0f, 1.0f)); + mNodeInputManager.AddInput(InputTypeBase::CONSTANT_VALUE, AddInput("Rate", 256.0f, 1, 1.0f, 2048.0f)); + mNodeInputManager.AddInput(InputTypeBase::CONSTANT_VALUE, AddInput("Velocity", 0.5f, 1, 0.0f, 1.0f)); + mNodeInputManager.AddInput(InputTypeBase::CONSTANT_VALUE, AddInput("Fade", 0.1f, 1, 0.0001f, 1.0f)); + mNodeInputManager.AddInput(InputTypeBase::CONSTANT_VALUE, AddInput("Attack", 50.0f, 1, 1.0f, 10000.0f)); + mNodeInputManager.AddInput(InputTypeBase::CONSTANT_VALUE, AddInput("Release", 50.0f, 1, 1.0f, 10000.0f)); + mNodeInputManager.AddCustom(InputTypeBase::CUSTOM_INTERP_RWA_MS); // 100 + mNodeInputManager.AddCustom(InputTypeBase::CUSTOM_INTERP_RWA_MS); // 101 AddOutput("Freq"); AddOutput("Volume"); @@ -63,9 +65,7 @@ namespace l::nodegraph { GraphControlEnvelope(NodeGraphBase* node) : GraphControlBase(node, "Envelope") { - mFreqTargetId = mNodeInputManager.AddInputBase(InputTypeBase::CONSTANT_VALUE, AddInput("Freq")); - mFreqId = mNodeInputManager.AddInputBase(InputTypeBase::CUSTOM_INTERP_RWA_MS); - mNodeInputManager.AddInputBase(InputTypeBase::CUSTOM_INTERP_TWEEN_MS); + mNodeInputManager.AddInput(InputTypeBase::CONSTANT_VALUE, AddInput("Freq")); mEnvelope = 0.0f; } @@ -75,8 +75,6 @@ namespace l::nodegraph { protected: bool mNoteOn = false; int32_t mFrameCount = 0; - int32_t mFreqTargetId = 0; - int32_t mFreqId = 0; int32_t mAttackFrames = 0; int32_t mReleaseFrames = 0; float mAttackFactor = 0.0f; @@ -98,16 +96,16 @@ namespace l::nodegraph { NodeGraphOp(node, "Arpeggio"), mNodeInputManager(*this) { - mNodeInputManager.AddInputBase(InputTypeBase::CONSTANT_VALUE, AddInput("Sync", 0.0f, 1, 0.0f, 1.0f)); - mNodeInputManager.AddInputBase(InputTypeBase::CONSTANT_VALUE, AddInput("Bpm", 60.0f, 1, 1.0f, 1000.0f)); - mNodeInputManager.AddInputBase(InputTypeBase::CONSTANT_VALUE, AddInput("Velocity", 0.5f, 1, 0.0f, 1.0f)); - mNodeInputManager.AddInputBase(InputTypeBase::CONSTANT_VALUE, AddInput("Fade", 1.0f, 1, 0.0001f, 1.0f)); - mNodeInputManager.AddInputBase(InputTypeBase::CONSTANT_VALUE, AddInput("Attack", 5.0f, 1, 1.0f, 10000.0f)); - mNodeInputManager.AddInputBase(InputTypeBase::CONSTANT_VALUE, AddInput("Release", 20.0f, 1, 1.0f, 10000.0f)); - mNodeInputManager.AddInputBase(InputTypeBase::CONSTANT_ARRAY, AddInput("Note On", l::audio::gNoNote_f, gPolyphony, -499.0, 500.0)); - mNodeInputManager.AddInputBase(InputTypeBase::CONSTANT_ARRAY, AddInput("Note Off", l::audio::gNoNote_f, gPolyphony, -499.0, 500.0)); - mNodeInputManager.AddInputBase(InputTypeBase::CUSTOM_INTERP_RWA_MS); - mNodeInputManager.AddInputBase(InputTypeBase::CUSTOM_INTERP_RWA_MS); + mNodeInputManager.AddInput(InputTypeBase::CONSTANT_VALUE, AddInput("Sync", 0.0f, 1, 0.0f, 1.0f)); + mNodeInputManager.AddInput(InputTypeBase::CONSTANT_VALUE, AddInput("Bpm", 60.0f, 1, 1.0f, 1000.0f)); + mNodeInputManager.AddInput(InputTypeBase::CONSTANT_VALUE, AddInput("Velocity", 0.5f, 1, 0.0f, 1.0f)); + mNodeInputManager.AddInput(InputTypeBase::CONSTANT_VALUE, AddInput("Fade", 1.0f, 1, 0.0001f, 1.0f)); + mNodeInputManager.AddInput(InputTypeBase::CONSTANT_VALUE, AddInput("Attack", 5.0f, 1, 1.0f, 10000.0f)); + mNodeInputManager.AddInput(InputTypeBase::CONSTANT_VALUE, AddInput("Release", 20.0f, 1, 1.0f, 10000.0f)); + mNodeInputManager.AddInput(InputTypeBase::CONSTANT_ARRAY, AddInput("Note On", l::audio::gNoNote_f, gPolyphony, -499.0, 500.0)); + mNodeInputManager.AddInput(InputTypeBase::CONSTANT_ARRAY, AddInput("Note Off", l::audio::gNoNote_f, gPolyphony, -499.0, 500.0)); + mNodeInputManager.AddCustom(InputTypeBase::CUSTOM_INTERP_RWA_MS); // 100 + mNodeInputManager.AddCustom(InputTypeBase::CUSTOM_INTERP_RWA_MS); // 101 AddOutput("Freq"); AddOutput("Volume"); @@ -123,6 +121,7 @@ namespace l::nodegraph { float mSamplesUntilUpdate = 0.0f; float mUpdateRate = 256.0f; + int32_t mUpdateCounter = 0; float mGainTarget = 0.0f; float mGain = 0.0f; diff --git a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpFilter.h b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpFilter.h index 39a78a0d..b0a1f0cb 100644 --- a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpFilter.h +++ b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpFilter.h @@ -37,10 +37,10 @@ namespace l::nodegraph { mDefaultOutStrings.clear(); mDefaultInData.clear(); - mNodeInputManager.AddInputBase(InputTypeBase::CONSTANT_VALUE, AddInput("Sync", 0.0f)); - mNodeInputManager.AddInputBase(InputTypeBase::SAMPLED, AddInput("In")); - mNodeInputManager.AddInputBase(InputTypeBase::INTERP_RWA, AddInput("Cutoff", 1.0f)); - mNodeInputManager.AddInputBase(InputTypeBase::INTERP_RWA, AddInput("Resonance", 0.0f)); + mNodeInputManager.AddInput(InputTypeBase::CONSTANT_VALUE, AddInput("Sync", 0.0f)); + mNodeInputManager.AddInput(InputTypeBase::SAMPLED, AddInput("In")); + mNodeInputManager.AddInput(InputTypeBase::INTERP_RWA, AddInput("Cutoff", 1.0f)); + mNodeInputManager.AddInput(InputTypeBase::INTERP_RWA, AddInput("Resonance", 0.0f)); AddOutput("Out", 0.0f); } diff --git a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpOutput.h b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpOutput.h index cd1807d9..eca34a8a 100644 --- a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpOutput.h +++ b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpOutput.h @@ -78,7 +78,7 @@ namespace l::nodegraph { mNodeInputManager(*this), mPlotSamples(plotSamples) { - mNodeInputManager.AddInputBase(InputTypeBase::SAMPLED, AddInput("Plot", 0.0f, 1, -1.0f, 1.0f)); + mNodeInputManager.AddInput(InputTypeBase::SAMPLED, AddInput("Plot", 0.0f, 1, -1.0f, 1.0f)); AddOutput("Plot", 0.0f, mPlotSamples); } virtual ~GraphOutputPlot() = default; diff --git a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpSignal.h b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpSignal.h index 1413e867..2eb71d47 100644 --- a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpSignal.h +++ b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpSignal.h @@ -32,11 +32,11 @@ namespace l::nodegraph { NodeGraphOp(node, name), mNodeInputManager(*this) { - mNodeInputManager.AddInputBase(InputTypeBase::CONSTANT_VALUE, AddInput("Sync", 0.0f, 1, 0.0f, 1.0f)); - mNodeInputManager.AddInputBase(InputTypeBase::CONSTANT_VALUE, AddInput("Rate", 256.0f, 1, 1.0f, 2048.0f)); - mNodeInputManager.AddInputBase(InputTypeBase::SAMPLED, AddInput("Freq", 0.0f, 1, 0.0f, 22050.0f)); - mNodeInputManager.AddInputBase(InputTypeBase::SAMPLED, AddInput("Volume", 0.0f, 1, 0.0f, 5.0f)); - mNodeInputManager.AddInputBase(InputTypeBase::CONSTANT_VALUE, AddInput("Smooth", 1.0f, 1, 0.0f, 1.0f)); + mNodeInputManager.AddInput(InputTypeBase::CONSTANT_VALUE, AddInput("Sync", 0.0f, 1, 0.0f, 1.0f)); + mNodeInputManager.AddInput(InputTypeBase::CONSTANT_VALUE, AddInput("Rate", 256.0f, 1, 1.0f, 2048.0f)); + mNodeInputManager.AddInput(InputTypeBase::SAMPLED_RWA, AddInput("Freq", 0.0f, 1, 0.0f, 22050.0f)); + mNodeInputManager.AddInput(InputTypeBase::SAMPLED_RWA, AddInput("Volume", 0.0f, 1, 0.0f, 5.0f)); + mNodeInputManager.AddInput(InputTypeBase::CONSTANT_VALUE, AddInput("Smooth", 1.0f, 1, 0.0f, 1.0f)); AddOutput("Out", 0.0f, 2); @@ -76,8 +76,8 @@ namespace l::nodegraph { GraphSignalSine2(NodeGraphBase* node) : GraphSignalBase(node, "Sine 2") { - mNodeInputManager.AddInputBase(InputTypeBase::CONSTANT_VALUE, AddInput("Fmod", 0.0f, 1, 0.0f, 1.0f)); - mNodeInputManager.AddInputBase(InputTypeBase::CONSTANT_VALUE, AddInput("Phase", 0.0f, 1, 0.0f, 1.0f)); + mNodeInputManager.AddInput(InputTypeBase::CONSTANT_VALUE, AddInput("Fmod", 0.0f, 1, 0.0f, 1.0f)); + mNodeInputManager.AddInput(InputTypeBase::CONSTANT_VALUE, AddInput("Phase", 0.0f, 1, 0.0f, 1.0f)); } virtual ~GraphSignalSine2() = default; @@ -119,8 +119,8 @@ namespace l::nodegraph { GraphSignalSaw2(NodeGraphBase* node) : GraphSignalBase(node, "Saw 2") { - mNodeInputManager.AddInputBase(InputTypeBase::INTERP_RWA, AddInput("Attenuation", 0.0f, 1, 0.0f, 1.0f)); - mNodeInputManager.AddInputBase(InputTypeBase::INTERP_RWA, AddInput("Cutoff", 0.0f, 1, 0.0f, 1.0f)); + mNodeInputManager.AddInput(InputTypeBase::INTERP_RWA, AddInput("Attenuation", 0.0f, 1, 0.0f, 1.0f)); + mNodeInputManager.AddInput(InputTypeBase::INTERP_RWA, AddInput("Cutoff", 0.0f, 1, 0.0f, 1.0f)); } virtual ~GraphSignalSaw2() = default; diff --git a/packages/nodegraph/source/common/core/NodeGraphBase.cpp b/packages/nodegraph/source/common/core/NodeGraphBase.cpp index cf9827ab..5b4a59bd 100644 --- a/packages/nodegraph/source/common/core/NodeGraphBase.cpp +++ b/packages/nodegraph/source/common/core/NodeGraphBase.cpp @@ -380,24 +380,26 @@ namespace l::nodegraph { /**********************************************************************************/ - int32_t NodeInputManager::AddInputBase(InputTypeBase type, int32_t inputIndex) { - if (type != InputTypeBase::CUSTOM_INTERP_RWA_MS && type != InputTypeBase::CUSTOM_INTERP_TWEEN && type != InputTypeBase::CUSTOM_INTERP_TWEEN_MS) { - ASSERT(inputIndex >= 0); - } - + int32_t NodeInputManager::AddInput(InputTypeBase type, int32_t inputIndex) { inputIndex = static_cast(mInputs.size()); mInputs.emplace_back(NodeGraphInputAccessor{ type, inputIndex }); - - if (type != InputTypeBase::CUSTOM_INTERP_RWA_MS && type != InputTypeBase::CUSTOM_INTERP_TWEEN && type != InputTypeBase::CUSTOM_INTERP_TWEEN_MS) { - mInputs.back().SetValue(mNodeGraphOperation.GetDefaultData(static_cast(inputIndex))); - mInputs.back().SetTarget(mNodeGraphOperation.GetDefaultData(static_cast(inputIndex))); - } + mInputs.back().SetValue(mNodeGraphOperation.GetDefaultData(static_cast(inputIndex))); + mInputs.back().SetTarget(mNodeGraphOperation.GetDefaultData(static_cast(inputIndex))); return inputIndex; } - void NodeInputManager::ProcessUpdate(std::vector& inputs, int32_t numSamples, float updateRate) { + int32_t NodeInputManager::AddCustom(InputTypeBase type) { + auto inputIndex = static_cast(mCustom.size()); + mCustom.emplace_back(NodeGraphInputAccessor{ type, inputIndex }); + return inputIndex + gCustomIndexBase; + } + + void NodeInputManager::BatchUpdate(std::vector& inputs, int32_t numSamples) { for (auto& input : mInputs) { - input.ProcessUpdate(inputs, numSamples, updateRate); + input.BatchUpdate(inputs, numSamples); + } + for (auto& custom : mCustom) { + custom.BatchUpdate(inputs, numSamples); } } @@ -405,33 +407,99 @@ namespace l::nodegraph { for (auto& input : mInputs) { input.NodeUpdate(inputs, updateRate); } + for (auto& custom : mCustom) { + custom.NodeUpdate(inputs, updateRate); + } } float NodeInputManager::GetValueNext(int32_t inputIndex) { - return mInputs.at(inputIndex).GetValueNext(); + if (inputIndex < gCustomIndexBase) { + return mInputs.at(inputIndex).GetValueNext(); + } + return mCustom.at(inputIndex - gCustomIndexBase).GetValueNext(); } float NodeInputManager::GetValue(int32_t inputIndex) { - return mInputs.at(inputIndex).GetValue(); + if (inputIndex < gCustomIndexBase) { + return mInputs.at(inputIndex).GetValue(); + } + return mCustom.at(inputIndex - gCustomIndexBase).GetValue(); } float NodeInputManager::GetArrayValue(int32_t inputIndex, int32_t arrayIndex) { - return mInputs.at(inputIndex).GetArrayValue(arrayIndex); + if (inputIndex < gCustomIndexBase) { + return mInputs.at(inputIndex).GetArrayValue(arrayIndex); + } + return mCustom.at(inputIndex - gCustomIndexBase).GetArrayValue(arrayIndex); } float* NodeInputManager::GetArray(int32_t inputIndex) { - return mInputs.at(inputIndex).GetArray(); + if (inputIndex < gCustomIndexBase) { + return mInputs.at(inputIndex).GetArray(); + } + return mCustom.at(inputIndex - gCustomIndexBase).GetArray(); + } + + void NodeInputManager::SetUpdateRate(int32_t inputIndex, float updateRate) { + if (inputIndex < gCustomIndexBase) { + mInputs.at(inputIndex).SetUpdateRate(updateRate); + } + mCustom.at(inputIndex - gCustomIndexBase).SetUpdateRate(updateRate); } void NodeInputManager::SetDuration(int32_t inputIndex, float value, float limit) { - mInputs.at(inputIndex).SetDuration(value, limit); + if (inputIndex < gCustomIndexBase) { + mInputs.at(inputIndex).SetDuration(value, limit); + } + mCustom.at(inputIndex - gCustomIndexBase).SetDuration(value, limit); } void NodeInputManager::SetTarget(int32_t inputIndex, float value) { - mInputs.at(inputIndex).SetTarget(value); + if (inputIndex < gCustomIndexBase) { + mInputs.at(inputIndex).SetTarget(value); + } + mCustom.at(inputIndex - gCustomIndexBase).SetTarget(value); } void NodeInputManager::SetValue(int32_t inputIndex, float value) { - mInputs.at(inputIndex).SetValue(value); + if (inputIndex < gCustomIndexBase) { + mInputs.at(inputIndex).SetValue(value); + } + mCustom.at(inputIndex - gCustomIndexBase).SetValue(value); + } + + /**********************************************************************************/ + + void NodeGraphOp2::Tick(int32_t, float) { + } + + int32_t NodeGraphOp2::AddInput( + InputTypeBase type, + std::string_view name, + float defaultValue, + int32_t size, + float boundMin, + float boundMax, + bool visible, + bool editable) { + return mNodeInputManager.AddInput(type, NodeGraphOp::AddInput(name, defaultValue, size, boundMin, boundMax, visible, editable)); + } + + int32_t NodeGraphOp2::AddConstant( + InputTypeBase type, + std::string_view name, + float defaultValue, + int32_t size, + float boundMin, + float boundMax, + bool visible, + bool editable) { + return mNodeInputManager.AddInput(type, NodeGraphOp::AddConstant(name, defaultValue, size, boundMin, boundMax, visible, editable)); + } + + int32_t NodeGraphOp2::AddCustom(InputTypeBase type) { + return mNodeInputManager.AddCustom(type); + } + } \ No newline at end of file diff --git a/packages/nodegraph/source/common/core/NodeGraphGroup.cpp b/packages/nodegraph/source/common/core/NodeGraphGroup.cpp index 5a39135c..190be5c9 100644 --- a/packages/nodegraph/source/common/core/NodeGraphGroup.cpp +++ b/packages/nodegraph/source/common/core/NodeGraphGroup.cpp @@ -14,8 +14,8 @@ namespace l::nodegraph { ASSERT(numInputSamples == numSamples); } - auto input = &inputs.at(i).Get(numInputSamples); - auto output = &outputs.at(i).Get(numSamples); + auto input = inputs.at(i).GetIterator(numInputSamples); + auto output = outputs.at(i).GetIterator(numSamples); for (int32_t j = 0; j < numSamples; j++) { *output++ = *input++; } diff --git a/packages/nodegraph/source/common/core/NodeGraphInput.cpp b/packages/nodegraph/source/common/core/NodeGraphInput.cpp index b0d7cfd7..20e015ba 100644 --- a/packages/nodegraph/source/common/core/NodeGraphInput.cpp +++ b/packages/nodegraph/source/common/core/NodeGraphInput.cpp @@ -10,7 +10,7 @@ namespace l::nodegraph { void NodeGraphInput::Reset() { if (mInputType == InputType::INPUT_NODE || mInputType == InputType::INPUT_VALUE) { mInput.mInputNode = nullptr; - mInputType = InputType::INPUT_EMPTY; + mInputType = InputType::INPUT_CONSTANT; mInputFromOutputChannel = 0; } } @@ -44,26 +44,34 @@ namespace l::nodegraph { return mInput.mInputFloatConstant; case InputType::INPUT_VALUE: return *mInput.mInputFloat; - case InputType::INPUT_EMPTY: - break; } return mInput.mInputFloatConstant; } - NodeDataIterator NodeGraphInput::GetIterator(int32_t size, float lod) { - if (mInputType == InputType::INPUT_NODE && mInput.mInputNode != nullptr) { - return mInput.mInputNode->GetOutputOf(mInputFromOutputChannel).GetIterator(); - } - else if (mInputType == InputType::INPUT_CONSTANT) { - return NodeDataIterator(&Get(1), 0.0f); - } - else { - if (mInputLod == 1.0f && lod > 1.0f) { - mInputLod = lod; + NodeDataIterator NodeGraphInput::GetIterator(int32_t size) { + switch (mInputType) { + case InputType::INPUT_NODE: + if (mInput.mInputNode != nullptr) { + return mInput.mInputNode->GetOutputOf(mInputFromOutputChannel).GetIterator(); + } + break; + case InputType::INPUT_ARRAY: + if (!mInputBuf) { + mInputBuf = std::make_unique>(); } - float stepPerIndex = size == 1 ? 0.0f : 1.0f / mInputLod; - return NodeDataIterator(&Get(size), stepPerIndex); + if (static_cast(mInputBuf->size()) < size) { + mInputBuf->resize(size); + for (size_t i = 0; i < mInputBuf->size(); i++) { + (*mInputBuf)[i] = 0.0f; + } + } + return NodeDataIterator(mInputBuf->data()); + case InputType::INPUT_VALUE: + return NodeDataIterator(mInput.mInputFloat, 0.0f); + case InputType::INPUT_CONSTANT: + return NodeDataIterator(&mInput.mInputFloatConstant, 0.0f); } + return NodeDataIterator(&mInput.mInputFloatConstant, 0.0f); } NodeDataIterator NodeGraphInput::GetArrayIterator() { @@ -87,14 +95,33 @@ namespace l::nodegraph { return 1; case InputType::INPUT_VALUE: return 1; - case InputType::INPUT_EMPTY: - break; } return 1; } /*********************************************************************************/ + void NodeGraphInputAccessor::SetUpdateRate(float updateRate) { + switch (mType) { + case InputTypeBase::INTERP_RWA: + case InputTypeBase::INTERP_RWA_MS: + mInput.mFilterRWA.SetRWAUpdateRate(updateRate); + break; + case InputTypeBase::CUSTOM_INTERP_TWEEN: + case InputTypeBase::CUSTOM_INTERP_TWEEN_MS: + mInput.mTween.Update(updateRate); + break; + case InputTypeBase::CUSTOM_INTERP_RWA_MS: // must set it manually + mInput.mFilterRWA.SetRWAUpdateRate(updateRate); + break; + case InputTypeBase::SAMPLED: + case InputTypeBase::CONSTANT_VALUE: + case InputTypeBase::CONSTANT_ARRAY: + case InputTypeBase::SAMPLED_RWA: + break; + } + } + void NodeGraphInputAccessor::SetDuration(int32_t ticks) { switch (mType) { case InputTypeBase::INTERP_RWA: @@ -109,6 +136,7 @@ namespace l::nodegraph { mInput.mTween.SetTweenLength(ticks); break; case InputTypeBase::SAMPLED: + case InputTypeBase::SAMPLED_RWA: case InputTypeBase::CONSTANT_VALUE: case InputTypeBase::CONSTANT_ARRAY: ASSERT(false) << "Failed to set convergence on a non interpolated input type"; @@ -130,6 +158,7 @@ namespace l::nodegraph { mInput.mTween.SetTweenLength(l::audio::GetAudioTicksFromMS(ms)); break; case InputTypeBase::SAMPLED: + case InputTypeBase::SAMPLED_RWA: case InputTypeBase::CONSTANT_VALUE: case InputTypeBase::CONSTANT_ARRAY: ASSERT(false) << "Failed to set convergence on a non interpolated input type"; @@ -149,6 +178,7 @@ namespace l::nodegraph { mInput.mTween.SetTarget(value); break; case InputTypeBase::SAMPLED: + case InputTypeBase::SAMPLED_RWA: case InputTypeBase::CONSTANT_VALUE: case InputTypeBase::CONSTANT_ARRAY: break; @@ -185,6 +215,8 @@ namespace l::nodegraph { case InputTypeBase::SAMPLED: case InputTypeBase::CONSTANT_ARRAY: return *mInput.mIterator++; + case InputTypeBase::SAMPLED_RWA: + return mInput.mIteratorRwa++; case InputTypeBase::CONSTANT_VALUE: return *mInput.mIterator; } @@ -204,6 +236,8 @@ namespace l::nodegraph { case InputTypeBase::CONSTANT_VALUE: case InputTypeBase::CONSTANT_ARRAY: return *mInput.mIterator; + case InputTypeBase::SAMPLED_RWA: + return *mInput.mIteratorRwa; } return 0.0f; } @@ -223,12 +257,11 @@ namespace l::nodegraph { } // run on each new batch call to setup input iterators for buffered data - void NodeGraphInputAccessor::ProcessUpdate(std::vector& input, int32_t numSamples, float updateRate) { + void NodeGraphInputAccessor::BatchUpdate(std::vector& input, int32_t numSamples) { switch (mType) { case InputTypeBase::INTERP_RWA: case InputTypeBase::INTERP_RWA_MS: case InputTypeBase::CUSTOM_INTERP_RWA_MS: - mInput.mFilterRWA.SetRWAUpdateRate(updateRate); break; case InputTypeBase::CUSTOM_INTERP_TWEEN: case InputTypeBase::CUSTOM_INTERP_TWEEN_MS: @@ -236,6 +269,9 @@ namespace l::nodegraph { case InputTypeBase::SAMPLED: mInput.mIterator = input.at(mInputIndex).GetIterator(numSamples); break; + case InputTypeBase::SAMPLED_RWA: + mInput.mIteratorRwa = input.at(mInputIndex).GetIterator(numSamples); + break; case InputTypeBase::CONSTANT_VALUE: mInput.mIterator.Reset(&input.at(mInputIndex).Get(), 1); break; @@ -250,7 +286,6 @@ namespace l::nodegraph { switch (mType) { case InputTypeBase::INTERP_RWA: case InputTypeBase::INTERP_RWA_MS: - mInput.mFilterRWA.SetRWAUpdateRate(updateRate); mInput.mFilterRWA.SetTarget(input.at(mInputIndex).Get()); break; case InputTypeBase::CUSTOM_INTERP_TWEEN: @@ -258,11 +293,11 @@ namespace l::nodegraph { mInput.mTween.Update(updateRate); break; case InputTypeBase::CUSTOM_INTERP_RWA_MS: // must set it manually - mInput.mFilterRWA.SetRWAUpdateRate(updateRate); break; case InputTypeBase::SAMPLED: case InputTypeBase::CONSTANT_VALUE: case InputTypeBase::CONSTANT_ARRAY: + case InputTypeBase::SAMPLED_RWA: break; } } diff --git a/packages/nodegraph/source/common/operations/NodeGraphOpControl.cpp b/packages/nodegraph/source/common/operations/NodeGraphOpControl.cpp index db1c952a..f2be2668 100644 --- a/packages/nodegraph/source/common/operations/NodeGraphOpControl.cpp +++ b/packages/nodegraph/source/common/operations/NodeGraphOpControl.cpp @@ -12,7 +12,7 @@ namespace l::nodegraph { /*********************************************************************/ void GraphControlBase::Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) { - mNodeInputManager.ProcessUpdate(inputs, numSamples, mUpdateRate); + mNodeInputManager.BatchUpdate(inputs, numSamples); float sync = inputs.at(0).Get(); mUpdateRate = mNodeInputManager.GetValue(1); @@ -20,8 +20,8 @@ namespace l::nodegraph { mSamplesUntilUpdate = 0; } - auto output0 = outputs.at(0).GetIterator(numSamples); - auto output1 = outputs.at(1).GetIterator(numSamples); + auto output0 = outputs.at(0).GetIterator(numSamples, 16.0f); + auto output1 = outputs.at(1).GetIterator(numSamples, 16.0f); mSamplesUntilUpdate = l::audio::BatchUpdate(mUpdateRate, mSamplesUntilUpdate, 0, numSamples, @@ -45,21 +45,24 @@ namespace l::nodegraph { /*********************************************************************/ - void GraphControlEnvelope::UpdateSignal(NodeInputManager& inputManager) { + void GraphControlEnvelope::UpdateSignal(NodeInputManager&) { - mFreqTarget = inputManager.GetValueNext(mFreqTargetId); + mFreqTarget = mNodeInputManager.GetValueNext(6); if (mFreqTarget != 0.0f) { - inputManager.SetTarget(mFreqId, mFreqTarget); + mNodeInputManager.SetTarget(100, mFreqTarget); } - inputManager.SetDuration(mFreqId, 100.0f * inputManager.GetValue(3)); - mAttackFrames = l::audio::GetAudioTicksFromMS(inputManager.GetValue(4)); - mReleaseFrames = l::audio::GetAudioTicksFromMS(inputManager.GetValue(5)); - mAttackFactor = inputManager.GetValue(4); - mReleaseFactor = inputManager.GetValue(5); + mNodeInputManager.SetDuration(100, 1000.0f * mNodeInputManager.GetValue(3)); + mAttackFrames = l::audio::GetAudioTicksFromMS(mNodeInputManager.GetValue(4)); + mReleaseFrames = l::audio::GetAudioTicksFromMS(mNodeInputManager.GetValue(5)); + mAttackFactor = mNodeInputManager.GetValue(4); + mReleaseFactor = mNodeInputManager.GetValue(5); + + mNodeInputManager.SetUpdateRate(100, 1.0f); + mNodeInputManager.SetUpdateRate(101, 1.0f); } - std::pair GraphControlEnvelope::ProcessSignal(NodeInputManager& inputManager) { - float velocity = inputManager.GetValueNext(2); + std::pair GraphControlEnvelope::ProcessSignal(NodeInputManager&) { + float velocity = mNodeInputManager.GetValueNext(2); float freq = 0.0f; bool noteOn = mFreqTarget != 0.0f; @@ -69,8 +72,8 @@ namespace l::nodegraph { mFreqTargetPrev = mFreqTarget; - mNodeInputManager.SetDuration(8, mAttackFactor); - mNodeInputManager.SetTarget(8, l::math::functions::sqrt(velocity)); + mNodeInputManager.SetDuration(101, mAttackFactor); + mNodeInputManager.SetTarget(101, l::math::functions::sqrt(velocity)); if (mFrameCount == 0) { // if note was off we set freq immediately @@ -81,8 +84,8 @@ namespace l::nodegraph { else if (!noteOn && mNoteOn) { mNoteOn = false; - mNodeInputManager.SetDuration(8, mReleaseFactor); - mNodeInputManager.SetTarget(8, 0.0f); + mNodeInputManager.SetDuration(101, mReleaseFactor); + mNodeInputManager.SetTarget(101, 0.0f); if (mFrameCount > 0 && mFrameCount < mAttackFrames + 1) { // still in attack so fast forward to release @@ -111,17 +114,17 @@ namespace l::nodegraph { } } - freq = inputManager.GetValueNext(mFreqId); + freq = mNodeInputManager.GetValueNext(100); - float envelope = mNodeInputManager.GetValueNext(8); + float envelope = mNodeInputManager.GetValueNext(101); return { freq, envelope }; } /*********************************************************************/ - void GraphControlArpeggio::UpdateSignal(NodeInputManager& inputManager) { - float attack = inputManager.GetValue(4); - float release = inputManager.GetValue(5); + void GraphControlArpeggio::UpdateSignal(NodeInputManager&) { + float attack = mNodeInputManager.GetValue(4); + float release = mNodeInputManager.GetValue(5); float attackFactor = l::audio::GetRWAFactorFromMS(attack, 0.001f, gArpeggioUpdateRate); float releaseFactor = l::audio::GetRWAFactorFromMS(release, 0.001f, gArpeggioUpdateRate); mGainAttack = attackFactor; @@ -129,31 +132,48 @@ namespace l::nodegraph { if (mNotes.empty()) { mGainTarget = 0.0f; + mNodeInputManager.SetDuration(100, release, 0.01f); + mNodeInputManager.SetDuration(101, release, 0.01f); + mNodeInputManager.SetTarget(100, 0.0f); + mNodeInputManager.SetTarget(101, 1.0f); } else { - mNoteIndex = mNoteIndex % mNotes.size(); - mFreqTarget = l::audio::GetFrequencyFromNote(static_cast(mNotes.at(mNoteIndex))); - mNoteIndex++; - - auto velocity = inputManager.GetValue(2); - auto fade = inputManager.GetValue(3); - - mGainTarget = 3.0f*velocity; - mFreqSmoothing = fade; - mFreqSmoothing *= mFreqSmoothing * 0.5f; - - inputManager.SetDuration(8, attack, 0.01f); - inputManager.SetDuration(9, attack + release, 0.01f); - inputManager.SetValue(8, 0.0f); - inputManager.SetValue(9, 1.0f); + if (mUpdateCounter % 2 == 0) { + mNoteIndex = mNoteIndex % mNotes.size(); + mFreqTarget = l::audio::GetFrequencyFromNote(static_cast(mNotes.at(mNoteIndex))); + mNoteIndex++; + + auto velocity = mNodeInputManager.GetValue(2); + auto fade = mNodeInputManager.GetValue(3); + + mGainTarget = 2.0f * velocity; + mFreqSmoothing = fade; + mFreqSmoothing *= mFreqSmoothing * 0.5f; + + mNodeInputManager.SetDuration(100, attack, 0.01f); + mNodeInputManager.SetDuration(101, attack + release, 0.01f); + mNodeInputManager.SetTarget(100, 1.0f); + mNodeInputManager.SetTarget(101, 0.0f); + //inputManager.SetValue(100, 0.0f); + //inputManager.SetValue(101, 1.0f); + + mUpdateCounter = 0; + } + else { + mNodeInputManager.SetDuration(100, release, 0.01f); + mNodeInputManager.SetDuration(101, release, 0.01f); + mNodeInputManager.SetTarget(100, 0.0f); + mNodeInputManager.SetTarget(101, 1.0f); + } + mUpdateCounter++; } } void GraphControlArpeggio::Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) { - mNodeInputManager.ProcessUpdate(inputs, numSamples, mUpdateRate); + mNodeInputManager.BatchUpdate(inputs, numSamples); float sync = mNodeInputManager.GetValue(0); float bpm = mNodeInputManager.GetValue(1); - mUpdateRate = 44100.0f * 60.0f / (4.0f * bpm); + mUpdateRate = 44100.0f * 60.0f / (2.0f * 4.0f * bpm); if (sync > 0.5f) { mSamplesUntilUpdate = 0; } @@ -200,17 +220,14 @@ namespace l::nodegraph { mUpdateRate = mNodeInputManager.GetValue(1); UpdateSignal(mNodeInputManager); - return mUpdateRate; }, [&](int32_t start, int32_t end, bool) { for (int32_t i = start; i < end; i++) { - mNodeInputManager.SetTarget(8, 1.0f); - mNodeInputManager.SetTarget(9, 0.0f); - float attack = mNodeInputManager.GetValueNext(8); - float release = mNodeInputManager.GetValueNext(9); - float gain = mGainTarget * (attack * release); - mGain += 0.25f * (gain - mGain); + float envelope = mNodeInputManager.GetValueNext(100); + //float release = mNodeInputManager.GetValueNext(101); + float gain = mGainTarget * envelope; + mGain += 0.0025f * (gain - mGain); mFreq += mFreqSmoothing * (mFreqTarget - mFreq); diff --git a/packages/nodegraph/source/common/operations/NodeGraphOpFilter.cpp b/packages/nodegraph/source/common/operations/NodeGraphOpFilter.cpp index 65f90a24..b41a498b 100644 --- a/packages/nodegraph/source/common/operations/NodeGraphOpFilter.cpp +++ b/packages/nodegraph/source/common/operations/NodeGraphOpFilter.cpp @@ -27,7 +27,7 @@ namespace l::nodegraph { } void GraphFilterBase::Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) { - mNodeInputManager.ProcessUpdate(inputs, numSamples, mUpdateRate); + mNodeInputManager.BatchUpdate(inputs, numSamples); mSync = inputs.at(0).Get(); if (mSync > 0.5f) { mNode->SetInput(1, 0.0f, numSamples); diff --git a/packages/nodegraph/source/common/operations/NodeGraphOpOutput.cpp b/packages/nodegraph/source/common/operations/NodeGraphOpOutput.cpp index 3e93f740..152bbb23 100644 --- a/packages/nodegraph/source/common/operations/NodeGraphOpOutput.cpp +++ b/packages/nodegraph/source/common/operations/NodeGraphOpOutput.cpp @@ -87,7 +87,7 @@ namespace l::nodegraph { /*********************************************************************/ void GraphOutputPlot::Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) { - mNodeInputManager.ProcessUpdate(inputs, numSamples, 1.0f); + mNodeInputManager.BatchUpdate(inputs, numSamples); int32_t outputSize = outputs.at(0).GetSize(); float* output = &outputs.at(0).Get(outputSize); diff --git a/packages/nodegraph/source/common/operations/NodeGraphOpSignal.cpp b/packages/nodegraph/source/common/operations/NodeGraphOpSignal.cpp index 65f5dcec..1ab21f80 100644 --- a/packages/nodegraph/source/common/operations/NodeGraphOpSignal.cpp +++ b/packages/nodegraph/source/common/operations/NodeGraphOpSignal.cpp @@ -11,7 +11,7 @@ namespace l::nodegraph { /*********************************************************************/ void GraphSignalBase::Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) { - mNodeInputManager.ProcessUpdate(inputs, numSamples, mUpdateRate); + mNodeInputManager.BatchUpdate(inputs, numSamples); float sync = mNodeInputManager.GetValueNext(0); if (sync > 0.5f) { @@ -105,6 +105,8 @@ namespace l::nodegraph { void GraphSignalSaw2::UpdateSignal(std::vector&, std::vector&) { mAttenuation = mNodeInputManager.GetValueNext(mNumDefaultInputs + 0); mCutoff = mNodeInputManager.GetValueNext(mNumDefaultInputs + 1); + mNodeInputManager.SetUpdateRate(mNumDefaultInputs + 0, mUpdateRate); + mNodeInputManager.SetUpdateRate(mNumDefaultInputs + 1, mUpdateRate); UpdateSaw(&mSaw, 0.00001f + 0.99999f * mAttenuation * mAttenuation * mAttenuation, mCutoff * mCutoff); } diff --git a/packages/nodegraph/tests/common/NodeGraphDataTest.cpp b/packages/nodegraph/tests/common/NodeGraphDataTest.cpp index 917cb66f..9ab41e84 100644 --- a/packages/nodegraph/tests/common/NodeGraphDataTest.cpp +++ b/packages/nodegraph/tests/common/NodeGraphDataTest.cpp @@ -10,28 +10,33 @@ using namespace l; using namespace l::nodegraph; -class TestOp : public NodeGraphOp { +class TestOp : public NodeGraphOp2 { public: TestOp(NodeGraphBase* node) : - NodeGraphOp(node, "TestOp"), - mNodeInputManager(*this) + NodeGraphOp2(node, "TestOp") { - mNodeInputManager.AddInputBase(InputTypeBase::SAMPLED, AddInput("In", 0.0f, 1, 0.0f, 1.0f)); - AddOutput("Out"); + AddInput(InputTypeBase::SAMPLED, "In 0", 0.0f, 1, 0.0f, 1.0f); + AddInput(InputTypeBase::SAMPLED_RWA, "In 1", 0.0f, 1, 0.0f, 1.0f); + AddOutput("Out 0"); + AddOutput("Out 1"); } virtual ~TestOp() = default; virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override { - mNodeInputManager.ProcessUpdate(inputs, numSamples, 4.0f); - auto in = inputs.at(0).GetIterator(numSamples); - auto out = outputs.at(0).GetIterator(numSamples, 4.0f); + mNodeInputManager.BatchUpdate(inputs, numSamples, mUpdateRate); + auto out0 = outputs.at(0).GetIterator(numSamples, 4.0f); + auto out1 = outputs.at(1).GetIterator(numSamples, 4.0f); for (int i = 0; i < numSamples; i++) { - *out++ = *in++; + float value0 = mNodeInputManager.GetValueNext(0); + float value1 = mNodeInputManager.GetValueNext(1); + *out0++ = value0; + *out1++ = value1; + + LOG(LogInfo) << "Node input(" << i << "): " << value0 << ", " << value1; } } protected: - NodeInputManager mNodeInputManager; }; @@ -41,20 +46,22 @@ TEST(NodeGraphData, Setup) { NodeGraph node0; NodeGraph node1; - auto input = node0.GetInputOf(0).GetIterator(16); - for (int i = 0; i < 16; i++) { - *input++ = i; - } - + node0.SetInput(0, 0.0f, 2); + node0.SetInput(1, 0.0f, 2); node1.SetInput(0, node0, 0); - node1.ProcessSubGraph(16); - - auto output = node1.GetOutputOf(0).GetIterator(); + node1.SetInput(1, node0, 1); - for (int i = 0; i < 16; i++) { - LOG(LogInfo) << "Output(" << i << "): " << *output++; + { + auto input0 = node0.GetInputOf(0).GetIterator(32); + auto input1 = node0.GetInputOf(1).GetIterator(32); + for (int i = 0; i < 32; i++) { + *input0++ = static_cast(i > 24 ? 24 : i); + *input1++ = static_cast(i > 24 ? 24 : i); + } } + node1.ProcessSubGraph(32); + return 0; } From ff7b0642ca27ba0e32a35af91b2b50b2cbe661c6 Mon Sep 17 00:00:00 2001 From: lnd3 Date: Tue, 24 Sep 2024 01:01:53 +0200 Subject: [PATCH 33/42] Fix error. --- packages/nodegraph/tests/common/NodeGraphDataTest.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/nodegraph/tests/common/NodeGraphDataTest.cpp b/packages/nodegraph/tests/common/NodeGraphDataTest.cpp index 9ab41e84..fa693fda 100644 --- a/packages/nodegraph/tests/common/NodeGraphDataTest.cpp +++ b/packages/nodegraph/tests/common/NodeGraphDataTest.cpp @@ -23,7 +23,7 @@ class TestOp : public NodeGraphOp2 { virtual ~TestOp() = default; virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override { - mNodeInputManager.BatchUpdate(inputs, numSamples, mUpdateRate); + mNodeInputManager.BatchUpdate(inputs, numSamples); auto out0 = outputs.at(0).GetIterator(numSamples, 4.0f); auto out1 = outputs.at(1).GetIterator(numSamples, 4.0f); From e392f13825a9eff0ab71c1702c49b2218167f075 Mon Sep 17 00:00:00 2001 From: lnd3 Date: Tue, 24 Sep 2024 09:37:31 +0200 Subject: [PATCH 34/42] Fix stuff. --- packages/nodegraph/CMakeLists.txt | 3 ++- .../include/nodegraph/core/NodeGraphBase.h | 6 +++--- .../include/nodegraph/core/NodeGraphInput.h | 20 ------------------- .../nodegraph/operations/NodeGraphOpSignal.h | 2 +- .../source/common/NodeGraphSchema.cpp | 2 +- .../source/common/core/NodeGraphBase.cpp | 10 +++++++--- .../source/common/core/NodeGraphInput.cpp | 3 ++- .../common/operations/NodeGraphOpSignal.cpp | 2 ++ .../tests/common/NodeGraphDataTest.cpp | 4 ++-- .../include/rendering/ui/UINodeEditor.h | 2 +- 10 files changed, 21 insertions(+), 33 deletions(-) diff --git a/packages/nodegraph/CMakeLists.txt b/packages/nodegraph/CMakeLists.txt index e93d5698..85803a43 100644 --- a/packages/nodegraph/CMakeLists.txt +++ b/packages/nodegraph/CMakeLists.txt @@ -7,8 +7,9 @@ set(deps testing memory - hid + math audio + hid ) bs_generate_package(nodegraph "tier1" "${deps}" "PortAudio") diff --git a/packages/nodegraph/include/nodegraph/core/NodeGraphBase.h b/packages/nodegraph/include/nodegraph/core/NodeGraphBase.h index a05bf208..39fd3e70 100644 --- a/packages/nodegraph/include/nodegraph/core/NodeGraphBase.h +++ b/packages/nodegraph/include/nodegraph/core/NodeGraphBase.h @@ -258,7 +258,7 @@ namespace l::nodegraph { virtual void Tick(int32_t, float) override; - int32_t AddInput( + int32_t AddInput2( InputTypeBase type, std::string_view name, float defaultValue = 0.0f, @@ -268,7 +268,7 @@ namespace l::nodegraph { bool visible = true, bool editable = true); - int32_t AddConstant( + int32_t AddConstant2( InputTypeBase type, std::string_view name, float defaultValue = 0.0f, @@ -278,7 +278,7 @@ namespace l::nodegraph { bool visible = true, bool editable = true); - int32_t AddCustom( + int32_t AddCustom2( InputTypeBase type); protected: diff --git a/packages/nodegraph/include/nodegraph/core/NodeGraphInput.h b/packages/nodegraph/include/nodegraph/core/NodeGraphInput.h index 6b04a6a6..0e3840d7 100644 --- a/packages/nodegraph/include/nodegraph/core/NodeGraphInput.h +++ b/packages/nodegraph/include/nodegraph/core/NodeGraphInput.h @@ -48,26 +48,6 @@ namespace l::nodegraph { mType(type), mInputIndex(inputIndex) { - switch(mType) { - case InputTypeBase::INTERP_RWA: - case InputTypeBase::INTERP_RWA_MS: - case InputTypeBase::CUSTOM_INTERP_RWA_MS: - //new (&s.vec) std::vector; - new (&mInput.mFilterRWA) l::audio::FilterRWA(); - - //mInput.mFilter = l::audio::FilterRWA(); - break; - case InputTypeBase::CUSTOM_INTERP_TWEEN: - case InputTypeBase::CUSTOM_INTERP_TWEEN_MS: - new (&mInput.mTween) l::math::tween::DynamicTween(); - break; - case InputTypeBase::SAMPLED: - case InputTypeBase::CONSTANT_VALUE: - case InputTypeBase::CONSTANT_ARRAY: - new (&mInput.mIterator) NodeDataIterator(nullptr); - //mInput.mIterator = NodeInputDataIterator(); - break; - } } ~NodeGraphInputAccessor() = default; diff --git a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpSignal.h b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpSignal.h index 2eb71d47..02ab526f 100644 --- a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpSignal.h +++ b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpSignal.h @@ -50,7 +50,7 @@ namespace l::nodegraph { } virtual ~GraphSignalBase() = default; - virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override final; + virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; virtual void ResetInput() {}; virtual void ResetSignal() {}; diff --git a/packages/nodegraph/source/common/NodeGraphSchema.cpp b/packages/nodegraph/source/common/NodeGraphSchema.cpp index 8b24f7c7..22852f55 100644 --- a/packages/nodegraph/source/common/NodeGraphSchema.cpp +++ b/packages/nodegraph/source/common/NodeGraphSchema.cpp @@ -189,7 +189,7 @@ namespace l::nodegraph { } void NodeGraphSchema::ForEachNodeType(std::function&)> cb) const { - for (auto it : mRegisteredNodeTypes) { + for (auto& it : mRegisteredNodeTypes) { cb(it.first, it.second); } } diff --git a/packages/nodegraph/source/common/core/NodeGraphBase.cpp b/packages/nodegraph/source/common/core/NodeGraphBase.cpp index 5b4a59bd..40cbc9cb 100644 --- a/packages/nodegraph/source/common/core/NodeGraphBase.cpp +++ b/packages/nodegraph/source/common/core/NodeGraphBase.cpp @@ -443,6 +443,7 @@ namespace l::nodegraph { void NodeInputManager::SetUpdateRate(int32_t inputIndex, float updateRate) { if (inputIndex < gCustomIndexBase) { mInputs.at(inputIndex).SetUpdateRate(updateRate); + return; } mCustom.at(inputIndex - gCustomIndexBase).SetUpdateRate(updateRate); } @@ -450,6 +451,7 @@ namespace l::nodegraph { void NodeInputManager::SetDuration(int32_t inputIndex, float value, float limit) { if (inputIndex < gCustomIndexBase) { mInputs.at(inputIndex).SetDuration(value, limit); + return; } mCustom.at(inputIndex - gCustomIndexBase).SetDuration(value, limit); } @@ -457,6 +459,7 @@ namespace l::nodegraph { void NodeInputManager::SetTarget(int32_t inputIndex, float value) { if (inputIndex < gCustomIndexBase) { mInputs.at(inputIndex).SetTarget(value); + return; } mCustom.at(inputIndex - gCustomIndexBase).SetTarget(value); } @@ -464,6 +467,7 @@ namespace l::nodegraph { void NodeInputManager::SetValue(int32_t inputIndex, float value) { if (inputIndex < gCustomIndexBase) { mInputs.at(inputIndex).SetValue(value); + return; } mCustom.at(inputIndex - gCustomIndexBase).SetValue(value); } @@ -474,7 +478,7 @@ namespace l::nodegraph { } - int32_t NodeGraphOp2::AddInput( + int32_t NodeGraphOp2::AddInput2( InputTypeBase type, std::string_view name, float defaultValue, @@ -486,7 +490,7 @@ namespace l::nodegraph { return mNodeInputManager.AddInput(type, NodeGraphOp::AddInput(name, defaultValue, size, boundMin, boundMax, visible, editable)); } - int32_t NodeGraphOp2::AddConstant( + int32_t NodeGraphOp2::AddConstant2( InputTypeBase type, std::string_view name, float defaultValue, @@ -498,7 +502,7 @@ namespace l::nodegraph { return mNodeInputManager.AddInput(type, NodeGraphOp::AddConstant(name, defaultValue, size, boundMin, boundMax, visible, editable)); } - int32_t NodeGraphOp2::AddCustom(InputTypeBase type) { + int32_t NodeGraphOp2::AddCustom2(InputTypeBase type) { return mNodeInputManager.AddCustom(type); } diff --git a/packages/nodegraph/source/common/core/NodeGraphInput.cpp b/packages/nodegraph/source/common/core/NodeGraphInput.cpp index 20e015ba..2e9e8c40 100644 --- a/packages/nodegraph/source/common/core/NodeGraphInput.cpp +++ b/packages/nodegraph/source/common/core/NodeGraphInput.cpp @@ -197,6 +197,7 @@ namespace l::nodegraph { mInput.mTween.Reset(value); break; case InputTypeBase::SAMPLED: + case InputTypeBase::SAMPLED_RWA: case InputTypeBase::CONSTANT_VALUE: case InputTypeBase::CONSTANT_ARRAY: break; @@ -295,9 +296,9 @@ namespace l::nodegraph { case InputTypeBase::CUSTOM_INTERP_RWA_MS: // must set it manually break; case InputTypeBase::SAMPLED: + case InputTypeBase::SAMPLED_RWA: case InputTypeBase::CONSTANT_VALUE: case InputTypeBase::CONSTANT_ARRAY: - case InputTypeBase::SAMPLED_RWA: break; } } diff --git a/packages/nodegraph/source/common/operations/NodeGraphOpSignal.cpp b/packages/nodegraph/source/common/operations/NodeGraphOpSignal.cpp index 1ab21f80..a1fc7225 100644 --- a/packages/nodegraph/source/common/operations/NodeGraphOpSignal.cpp +++ b/packages/nodegraph/source/common/operations/NodeGraphOpSignal.cpp @@ -107,6 +107,8 @@ namespace l::nodegraph { mCutoff = mNodeInputManager.GetValueNext(mNumDefaultInputs + 1); mNodeInputManager.SetUpdateRate(mNumDefaultInputs + 0, mUpdateRate); mNodeInputManager.SetUpdateRate(mNumDefaultInputs + 1, mUpdateRate); + mNodeInputManager.SetDuration(mNumDefaultInputs + 0, 1.0f); + mNodeInputManager.SetDuration(mNumDefaultInputs + 1, 1.0f); UpdateSaw(&mSaw, 0.00001f + 0.99999f * mAttenuation * mAttenuation * mAttenuation, mCutoff * mCutoff); } diff --git a/packages/nodegraph/tests/common/NodeGraphDataTest.cpp b/packages/nodegraph/tests/common/NodeGraphDataTest.cpp index fa693fda..f89eac7d 100644 --- a/packages/nodegraph/tests/common/NodeGraphDataTest.cpp +++ b/packages/nodegraph/tests/common/NodeGraphDataTest.cpp @@ -15,8 +15,8 @@ class TestOp : public NodeGraphOp2 { TestOp(NodeGraphBase* node) : NodeGraphOp2(node, "TestOp") { - AddInput(InputTypeBase::SAMPLED, "In 0", 0.0f, 1, 0.0f, 1.0f); - AddInput(InputTypeBase::SAMPLED_RWA, "In 1", 0.0f, 1, 0.0f, 1.0f); + AddInput2(InputTypeBase::SAMPLED, "In 0", 0.0f, 1, 0.0f, 1.0f); + AddInput2(InputTypeBase::SAMPLED_RWA, "In 1", 0.0f, 1, 0.0f, 1.0f); AddOutput("Out 0"); AddOutput("Out 1"); } diff --git a/packages/rendering/include/rendering/ui/UINodeEditor.h b/packages/rendering/include/rendering/ui/UINodeEditor.h index cccac28a..673052b5 100644 --- a/packages/rendering/include/rendering/ui/UINodeEditor.h +++ b/packages/rendering/include/rendering/ui/UINodeEditor.h @@ -45,7 +45,7 @@ namespace l::ui { mNGSchema->ForEachNodeType([&](std::string_view typeName, const std::vector& types) { if (typeName.empty() || ImGui::TreeNode(typeName.data())) { - for (auto it : types) { + for (auto& it : types) { if (ImGui::MenuItem(it.GetName().data())) { ImVec2 p = ImVec2(mUIInput.mCurPos.x - mUIWindow.GetPosition().x, mUIInput.mCurPos.y - mUIWindow.GetPosition().y); p.x -= mUIRoot->GetPosition().x; From e4aaf690d0e28e3b0047b723001b374e39e24586 Mon Sep 17 00:00:00 2001 From: lnd3 Date: Tue, 24 Sep 2024 10:49:16 +0200 Subject: [PATCH 35/42] Remove the weird rwa interpolators. --- .../include/nodegraph/core/NodeGraphData.h | 8 ++-- .../nodegraph/operations/NodeGraphOpControl.h | 26 +++++------ .../nodegraph/operations/NodeGraphOpFilter.h | 6 +-- .../nodegraph/operations/NodeGraphOpSignal.h | 14 +++--- .../source/common/core/NodeGraphInput.cpp | 44 +------------------ .../common/operations/NodeGraphOpControl.cpp | 25 +++++------ .../common/operations/NodeGraphOpFilter.cpp | 8 ++-- .../common/operations/NodeGraphOpSignal.cpp | 2 +- 8 files changed, 45 insertions(+), 88 deletions(-) diff --git a/packages/nodegraph/include/nodegraph/core/NodeGraphData.h b/packages/nodegraph/include/nodegraph/core/NodeGraphData.h index 704d4ae8..04dd9d6f 100644 --- a/packages/nodegraph/include/nodegraph/core/NodeGraphData.h +++ b/packages/nodegraph/include/nodegraph/core/NodeGraphData.h @@ -76,6 +76,9 @@ namespace l::nodegraph { } float GetStepsPerIncrement() { + if (mIncrement == 0.0f) { + return 0.0f; + } return 1.0f / mIncrement; } protected: @@ -100,7 +103,7 @@ namespace l::nodegraph { void Reset(NodeDataIterator&& iterator) { mIterator = std::move(iterator); mRwa.Value() = *mIterator; - mRwa.SetConvergenceInTicks(iterator.GetStepsPerIncrement(), 0.35f); + mRwa.SetConvergenceInTicks(l::math::functions::max(4.0f, iterator.GetStepsPerIncrement()), 0.35f); } protected: NodeDataIterator mIterator; @@ -110,9 +113,6 @@ namespace l::nodegraph { enum class InputTypeBase { SAMPLED = 0, // todo: make it interpolate in smaller custom buffers SAMPLED_RWA, // todo: add a smoothed sampled variant. Will replace interp_rwa and interp_rwa_ms as we will add a separate config for passing ticks or millis - INTERP_RWA, // TBR - INTERP_RWA_MS, // TBR - CONSTANT_VALUE, // Will basically be replace by sampled as it should be able to handle 1-sized arrays CONSTANT_ARRAY, // Same here, will be replaced CUSTOM_INTERP_TWEEN, // custom input vars should not be used at all CUSTOM_INTERP_TWEEN_MS, diff --git a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpControl.h b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpControl.h index 9ad3cbd8..859b94dd 100644 --- a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpControl.h +++ b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpControl.h @@ -32,12 +32,12 @@ namespace l::nodegraph { NodeGraphOp(node, name), mNodeInputManager(*this) { - mNodeInputManager.AddInput(InputTypeBase::CONSTANT_VALUE, AddInput("Sync", 0.0f, 1, 0.0f, 1.0f)); - mNodeInputManager.AddInput(InputTypeBase::CONSTANT_VALUE, AddInput("Rate", 256.0f, 1, 1.0f, 2048.0f)); - mNodeInputManager.AddInput(InputTypeBase::CONSTANT_VALUE, AddInput("Velocity", 0.5f, 1, 0.0f, 1.0f)); - mNodeInputManager.AddInput(InputTypeBase::CONSTANT_VALUE, AddInput("Fade", 0.1f, 1, 0.0001f, 1.0f)); - mNodeInputManager.AddInput(InputTypeBase::CONSTANT_VALUE, AddInput("Attack", 50.0f, 1, 1.0f, 10000.0f)); - mNodeInputManager.AddInput(InputTypeBase::CONSTANT_VALUE, AddInput("Release", 50.0f, 1, 1.0f, 10000.0f)); + mNodeInputManager.AddInput(InputTypeBase::SAMPLED, AddInput("Sync", 0.0f, 1, 0.0f, 1.0f)); + mNodeInputManager.AddInput(InputTypeBase::SAMPLED, AddInput("Rate", 256.0f, 1, 1.0f, 2048.0f)); + mNodeInputManager.AddInput(InputTypeBase::SAMPLED, AddInput("Velocity", 0.5f, 1, 0.0f, 1.0f)); + mNodeInputManager.AddInput(InputTypeBase::SAMPLED, AddInput("Fade", 0.1f, 1, 0.0001f, 1.0f)); + mNodeInputManager.AddInput(InputTypeBase::SAMPLED, AddInput("Attack", 50.0f, 1, 1.0f, 10000.0f)); + mNodeInputManager.AddInput(InputTypeBase::SAMPLED, AddInput("Release", 50.0f, 1, 1.0f, 10000.0f)); mNodeInputManager.AddCustom(InputTypeBase::CUSTOM_INTERP_RWA_MS); // 100 mNodeInputManager.AddCustom(InputTypeBase::CUSTOM_INTERP_RWA_MS); // 101 @@ -65,7 +65,7 @@ namespace l::nodegraph { GraphControlEnvelope(NodeGraphBase* node) : GraphControlBase(node, "Envelope") { - mNodeInputManager.AddInput(InputTypeBase::CONSTANT_VALUE, AddInput("Freq")); + mNodeInputManager.AddInput(InputTypeBase::SAMPLED_RWA, AddInput("Freq")); mEnvelope = 0.0f; } @@ -96,12 +96,12 @@ namespace l::nodegraph { NodeGraphOp(node, "Arpeggio"), mNodeInputManager(*this) { - mNodeInputManager.AddInput(InputTypeBase::CONSTANT_VALUE, AddInput("Sync", 0.0f, 1, 0.0f, 1.0f)); - mNodeInputManager.AddInput(InputTypeBase::CONSTANT_VALUE, AddInput("Bpm", 60.0f, 1, 1.0f, 1000.0f)); - mNodeInputManager.AddInput(InputTypeBase::CONSTANT_VALUE, AddInput("Velocity", 0.5f, 1, 0.0f, 1.0f)); - mNodeInputManager.AddInput(InputTypeBase::CONSTANT_VALUE, AddInput("Fade", 1.0f, 1, 0.0001f, 1.0f)); - mNodeInputManager.AddInput(InputTypeBase::CONSTANT_VALUE, AddInput("Attack", 5.0f, 1, 1.0f, 10000.0f)); - mNodeInputManager.AddInput(InputTypeBase::CONSTANT_VALUE, AddInput("Release", 20.0f, 1, 1.0f, 10000.0f)); + mNodeInputManager.AddInput(InputTypeBase::SAMPLED, AddInput("Sync", 0.0f, 1, 0.0f, 1.0f)); + mNodeInputManager.AddInput(InputTypeBase::SAMPLED_RWA, AddInput("Bpm", 60.0f, 1, 1.0f, 1000.0f)); + mNodeInputManager.AddInput(InputTypeBase::SAMPLED, AddInput("Velocity", 0.5f, 1, 0.0f, 1.0f)); + mNodeInputManager.AddInput(InputTypeBase::SAMPLED, AddInput("Fade", 1.0f, 1, 0.0001f, 1.0f)); + mNodeInputManager.AddInput(InputTypeBase::SAMPLED, AddInput("Attack", 5.0f, 1, 1.0f, 10000.0f)); + mNodeInputManager.AddInput(InputTypeBase::SAMPLED, AddInput("Release", 20.0f, 1, 1.0f, 10000.0f)); mNodeInputManager.AddInput(InputTypeBase::CONSTANT_ARRAY, AddInput("Note On", l::audio::gNoNote_f, gPolyphony, -499.0, 500.0)); mNodeInputManager.AddInput(InputTypeBase::CONSTANT_ARRAY, AddInput("Note Off", l::audio::gNoNote_f, gPolyphony, -499.0, 500.0)); mNodeInputManager.AddCustom(InputTypeBase::CUSTOM_INTERP_RWA_MS); // 100 diff --git a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpFilter.h b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpFilter.h index b0a1f0cb..db6c5db1 100644 --- a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpFilter.h +++ b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpFilter.h @@ -37,10 +37,10 @@ namespace l::nodegraph { mDefaultOutStrings.clear(); mDefaultInData.clear(); - mNodeInputManager.AddInput(InputTypeBase::CONSTANT_VALUE, AddInput("Sync", 0.0f)); + mNodeInputManager.AddInput(InputTypeBase::SAMPLED, AddInput("Sync", 0.0f)); mNodeInputManager.AddInput(InputTypeBase::SAMPLED, AddInput("In")); - mNodeInputManager.AddInput(InputTypeBase::INTERP_RWA, AddInput("Cutoff", 1.0f)); - mNodeInputManager.AddInput(InputTypeBase::INTERP_RWA, AddInput("Resonance", 0.0f)); + mNodeInputManager.AddInput(InputTypeBase::SAMPLED_RWA, AddInput("Cutoff", 1.0f)); + mNodeInputManager.AddInput(InputTypeBase::SAMPLED_RWA, AddInput("Resonance", 0.0f)); AddOutput("Out", 0.0f); } diff --git a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpSignal.h b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpSignal.h index 02ab526f..95ea00c9 100644 --- a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpSignal.h +++ b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpSignal.h @@ -32,11 +32,11 @@ namespace l::nodegraph { NodeGraphOp(node, name), mNodeInputManager(*this) { - mNodeInputManager.AddInput(InputTypeBase::CONSTANT_VALUE, AddInput("Sync", 0.0f, 1, 0.0f, 1.0f)); - mNodeInputManager.AddInput(InputTypeBase::CONSTANT_VALUE, AddInput("Rate", 256.0f, 1, 1.0f, 2048.0f)); + mNodeInputManager.AddInput(InputTypeBase::SAMPLED, AddInput("Sync", 0.0f, 1, 0.0f, 1.0f)); + mNodeInputManager.AddInput(InputTypeBase::SAMPLED, AddInput("Rate", 256.0f, 1, 1.0f, 2048.0f)); mNodeInputManager.AddInput(InputTypeBase::SAMPLED_RWA, AddInput("Freq", 0.0f, 1, 0.0f, 22050.0f)); mNodeInputManager.AddInput(InputTypeBase::SAMPLED_RWA, AddInput("Volume", 0.0f, 1, 0.0f, 5.0f)); - mNodeInputManager.AddInput(InputTypeBase::CONSTANT_VALUE, AddInput("Smooth", 1.0f, 1, 0.0f, 1.0f)); + mNodeInputManager.AddInput(InputTypeBase::SAMPLED, AddInput("Smooth", 1.0f, 1, 0.0f, 1.0f)); AddOutput("Out", 0.0f, 2); @@ -76,8 +76,8 @@ namespace l::nodegraph { GraphSignalSine2(NodeGraphBase* node) : GraphSignalBase(node, "Sine 2") { - mNodeInputManager.AddInput(InputTypeBase::CONSTANT_VALUE, AddInput("Fmod", 0.0f, 1, 0.0f, 1.0f)); - mNodeInputManager.AddInput(InputTypeBase::CONSTANT_VALUE, AddInput("Phase", 0.0f, 1, 0.0f, 1.0f)); + mNodeInputManager.AddInput(InputTypeBase::SAMPLED_RWA, AddInput("Fmod", 0.0f, 1, 0.0f, 1.0f)); + mNodeInputManager.AddInput(InputTypeBase::SAMPLED_RWA, AddInput("Phase", 0.0f, 1, 0.0f, 1.0f)); } virtual ~GraphSignalSine2() = default; @@ -119,8 +119,8 @@ namespace l::nodegraph { GraphSignalSaw2(NodeGraphBase* node) : GraphSignalBase(node, "Saw 2") { - mNodeInputManager.AddInput(InputTypeBase::INTERP_RWA, AddInput("Attenuation", 0.0f, 1, 0.0f, 1.0f)); - mNodeInputManager.AddInput(InputTypeBase::INTERP_RWA, AddInput("Cutoff", 0.0f, 1, 0.0f, 1.0f)); + mNodeInputManager.AddInput(InputTypeBase::SAMPLED_RWA, AddInput("Attenuation", 0.0f, 1, 0.0f, 1.0f)); + mNodeInputManager.AddInput(InputTypeBase::SAMPLED_RWA, AddInput("Cutoff", 0.0f, 1, 0.0f, 1.0f)); } virtual ~GraphSignalSaw2() = default; diff --git a/packages/nodegraph/source/common/core/NodeGraphInput.cpp b/packages/nodegraph/source/common/core/NodeGraphInput.cpp index 2e9e8c40..e18e8f69 100644 --- a/packages/nodegraph/source/common/core/NodeGraphInput.cpp +++ b/packages/nodegraph/source/common/core/NodeGraphInput.cpp @@ -103,10 +103,6 @@ namespace l::nodegraph { void NodeGraphInputAccessor::SetUpdateRate(float updateRate) { switch (mType) { - case InputTypeBase::INTERP_RWA: - case InputTypeBase::INTERP_RWA_MS: - mInput.mFilterRWA.SetRWAUpdateRate(updateRate); - break; case InputTypeBase::CUSTOM_INTERP_TWEEN: case InputTypeBase::CUSTOM_INTERP_TWEEN_MS: mInput.mTween.Update(updateRate); @@ -115,7 +111,6 @@ namespace l::nodegraph { mInput.mFilterRWA.SetRWAUpdateRate(updateRate); break; case InputTypeBase::SAMPLED: - case InputTypeBase::CONSTANT_VALUE: case InputTypeBase::CONSTANT_ARRAY: case InputTypeBase::SAMPLED_RWA: break; @@ -124,10 +119,6 @@ namespace l::nodegraph { void NodeGraphInputAccessor::SetDuration(int32_t ticks) { switch (mType) { - case InputTypeBase::INTERP_RWA: - mInput.mFilterRWA.SetConvergenceInTicks(static_cast(ticks)); - break; - case InputTypeBase::INTERP_RWA_MS: case InputTypeBase::CUSTOM_INTERP_RWA_MS: mInput.mFilterRWA.SetConvergenceInTicks(static_cast(ticks)); break; @@ -137,19 +128,13 @@ namespace l::nodegraph { break; case InputTypeBase::SAMPLED: case InputTypeBase::SAMPLED_RWA: - case InputTypeBase::CONSTANT_VALUE: case InputTypeBase::CONSTANT_ARRAY: - ASSERT(false) << "Failed to set convergence on a non interpolated input type"; break; } } void NodeGraphInputAccessor::SetDuration(float ms, float limit) { switch (mType) { - case InputTypeBase::INTERP_RWA: - mInput.mFilterRWA.SetConvergenceInMs(ms, limit); - break; - case InputTypeBase::INTERP_RWA_MS: case InputTypeBase::CUSTOM_INTERP_RWA_MS: mInput.mFilterRWA.SetConvergenceInMs(ms, limit); break; @@ -159,17 +144,13 @@ namespace l::nodegraph { break; case InputTypeBase::SAMPLED: case InputTypeBase::SAMPLED_RWA: - case InputTypeBase::CONSTANT_VALUE: case InputTypeBase::CONSTANT_ARRAY: - ASSERT(false) << "Failed to set convergence on a non interpolated input type"; break; } } void NodeGraphInputAccessor::SetTarget(float value) { switch (mType) { - case InputTypeBase::INTERP_RWA: - case InputTypeBase::INTERP_RWA_MS: case InputTypeBase::CUSTOM_INTERP_RWA_MS: mInput.mFilterRWA.SetTarget(value); break; @@ -179,7 +160,6 @@ namespace l::nodegraph { break; case InputTypeBase::SAMPLED: case InputTypeBase::SAMPLED_RWA: - case InputTypeBase::CONSTANT_VALUE: case InputTypeBase::CONSTANT_ARRAY: break; } @@ -187,8 +167,6 @@ namespace l::nodegraph { void NodeGraphInputAccessor::SetValue(float value) { switch (mType) { - case InputTypeBase::INTERP_RWA: - case InputTypeBase::INTERP_RWA_MS: case InputTypeBase::CUSTOM_INTERP_RWA_MS: mInput.mFilterRWA.Value() = value; break; @@ -198,7 +176,6 @@ namespace l::nodegraph { break; case InputTypeBase::SAMPLED: case InputTypeBase::SAMPLED_RWA: - case InputTypeBase::CONSTANT_VALUE: case InputTypeBase::CONSTANT_ARRAY: break; } @@ -206,8 +183,6 @@ namespace l::nodegraph { float NodeGraphInputAccessor::GetValueNext() { switch (mType) { - case InputTypeBase::INTERP_RWA: - case InputTypeBase::INTERP_RWA_MS: case InputTypeBase::CUSTOM_INTERP_RWA_MS: return mInput.mFilterRWA.Next(); case InputTypeBase::CUSTOM_INTERP_TWEEN: @@ -218,23 +193,18 @@ namespace l::nodegraph { return *mInput.mIterator++; case InputTypeBase::SAMPLED_RWA: return mInput.mIteratorRwa++; - case InputTypeBase::CONSTANT_VALUE: - return *mInput.mIterator; } return 0.0f; } float NodeGraphInputAccessor::GetValue() { switch (mType) { - case InputTypeBase::INTERP_RWA: - case InputTypeBase::INTERP_RWA_MS: case InputTypeBase::CUSTOM_INTERP_RWA_MS: return mInput.mFilterRWA.Value(); case InputTypeBase::CUSTOM_INTERP_TWEEN: case InputTypeBase::CUSTOM_INTERP_TWEEN_MS: return mInput.mTween.Value(); case InputTypeBase::SAMPLED: - case InputTypeBase::CONSTANT_VALUE: case InputTypeBase::CONSTANT_ARRAY: return *mInput.mIterator; case InputTypeBase::SAMPLED_RWA: @@ -260,10 +230,7 @@ namespace l::nodegraph { // run on each new batch call to setup input iterators for buffered data void NodeGraphInputAccessor::BatchUpdate(std::vector& input, int32_t numSamples) { switch (mType) { - case InputTypeBase::INTERP_RWA: - case InputTypeBase::INTERP_RWA_MS: case InputTypeBase::CUSTOM_INTERP_RWA_MS: - break; case InputTypeBase::CUSTOM_INTERP_TWEEN: case InputTypeBase::CUSTOM_INTERP_TWEEN_MS: break; @@ -273,9 +240,6 @@ namespace l::nodegraph { case InputTypeBase::SAMPLED_RWA: mInput.mIteratorRwa = input.at(mInputIndex).GetIterator(numSamples); break; - case InputTypeBase::CONSTANT_VALUE: - mInput.mIterator.Reset(&input.at(mInputIndex).Get(), 1); - break; case InputTypeBase::CONSTANT_ARRAY: mInput.mIterator = input.at(mInputIndex).GetArrayIterator(); break; @@ -283,21 +247,15 @@ namespace l::nodegraph { } // run on each node update (can be node specific) and will update node rwa filters - void NodeGraphInputAccessor::NodeUpdate(std::vector& input, float updateRate) { + void NodeGraphInputAccessor::NodeUpdate(std::vector&, float updateRate) { switch (mType) { - case InputTypeBase::INTERP_RWA: - case InputTypeBase::INTERP_RWA_MS: - mInput.mFilterRWA.SetTarget(input.at(mInputIndex).Get()); - break; case InputTypeBase::CUSTOM_INTERP_TWEEN: case InputTypeBase::CUSTOM_INTERP_TWEEN_MS: mInput.mTween.Update(updateRate); break; case InputTypeBase::CUSTOM_INTERP_RWA_MS: // must set it manually - break; case InputTypeBase::SAMPLED: case InputTypeBase::SAMPLED_RWA: - case InputTypeBase::CONSTANT_VALUE: case InputTypeBase::CONSTANT_ARRAY: break; } diff --git a/packages/nodegraph/source/common/operations/NodeGraphOpControl.cpp b/packages/nodegraph/source/common/operations/NodeGraphOpControl.cpp index f2be2668..f292b7d9 100644 --- a/packages/nodegraph/source/common/operations/NodeGraphOpControl.cpp +++ b/packages/nodegraph/source/common/operations/NodeGraphOpControl.cpp @@ -14,8 +14,8 @@ namespace l::nodegraph { void GraphControlBase::Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) { mNodeInputManager.BatchUpdate(inputs, numSamples); - float sync = inputs.at(0).Get(); - mUpdateRate = mNodeInputManager.GetValue(1); + float sync = mNodeInputManager.GetValueNext(0); + mUpdateRate = mNodeInputManager.GetValueNext(1); if (sync > 0.5f) { mSamplesUntilUpdate = 0; } @@ -51,11 +51,11 @@ namespace l::nodegraph { if (mFreqTarget != 0.0f) { mNodeInputManager.SetTarget(100, mFreqTarget); } - mNodeInputManager.SetDuration(100, 1000.0f * mNodeInputManager.GetValue(3)); - mAttackFrames = l::audio::GetAudioTicksFromMS(mNodeInputManager.GetValue(4)); - mReleaseFrames = l::audio::GetAudioTicksFromMS(mNodeInputManager.GetValue(5)); - mAttackFactor = mNodeInputManager.GetValue(4); - mReleaseFactor = mNodeInputManager.GetValue(5); + mNodeInputManager.SetDuration(100, 1000.0f * mNodeInputManager.GetValueNext(3)); + mAttackFrames = l::audio::GetAudioTicksFromMS(mNodeInputManager.GetValueNext(4)); + mReleaseFrames = l::audio::GetAudioTicksFromMS(mNodeInputManager.GetValueNext(5)); + mAttackFactor = mNodeInputManager.GetValueNext(4); + mReleaseFactor = mNodeInputManager.GetValueNext(5); mNodeInputManager.SetUpdateRate(100, 1.0f); mNodeInputManager.SetUpdateRate(101, 1.0f); @@ -143,8 +143,8 @@ namespace l::nodegraph { mFreqTarget = l::audio::GetFrequencyFromNote(static_cast(mNotes.at(mNoteIndex))); mNoteIndex++; - auto velocity = mNodeInputManager.GetValue(2); - auto fade = mNodeInputManager.GetValue(3); + auto velocity = mNodeInputManager.GetValueNext(2); + auto fade = mNodeInputManager.GetValueNext(3); mGainTarget = 2.0f * velocity; mFreqSmoothing = fade; @@ -171,8 +171,8 @@ namespace l::nodegraph { void GraphControlArpeggio::Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) { mNodeInputManager.BatchUpdate(inputs, numSamples); - float sync = mNodeInputManager.GetValue(0); - float bpm = mNodeInputManager.GetValue(1); + float sync = mNodeInputManager.GetValueNext(0); + float bpm = mNodeInputManager.GetValueNext(1); mUpdateRate = 44100.0f * 60.0f / (2.0f * 4.0f * bpm); if (sync > 0.5f) { mSamplesUntilUpdate = 0; @@ -217,7 +217,7 @@ namespace l::nodegraph { mSamplesUntilUpdate = l::audio::BatchUpdate(mUpdateRate, mSamplesUntilUpdate, 0, numSamples, [&]() { mNodeInputManager.NodeUpdate(inputs, mUpdateRate); - mUpdateRate = mNodeInputManager.GetValue(1); + mUpdateRate = mNodeInputManager.GetValueNext(1); UpdateSignal(mNodeInputManager); return mUpdateRate; @@ -225,7 +225,6 @@ namespace l::nodegraph { [&](int32_t start, int32_t end, bool) { for (int32_t i = start; i < end; i++) { float envelope = mNodeInputManager.GetValueNext(100); - //float release = mNodeInputManager.GetValueNext(101); float gain = mGainTarget * envelope; mGain += 0.0025f * (gain - mGain); diff --git a/packages/nodegraph/source/common/operations/NodeGraphOpFilter.cpp b/packages/nodegraph/source/common/operations/NodeGraphOpFilter.cpp index b41a498b..a900be4f 100644 --- a/packages/nodegraph/source/common/operations/NodeGraphOpFilter.cpp +++ b/packages/nodegraph/source/common/operations/NodeGraphOpFilter.cpp @@ -33,7 +33,7 @@ namespace l::nodegraph { mNode->SetInput(1, 0.0f, numSamples); } - auto output = &outputs.at(0).Get(numSamples); + auto output = outputs.at(0).GetIterator(numSamples); mSamplesUntilUpdate = l::audio::BatchUpdate(mUpdateRate, mSamplesUntilUpdate, 0, numSamples, [&]() { @@ -103,9 +103,9 @@ namespace l::nodegraph { } } - void GraphFilterChamberlain2pole::UpdateSignal(std::vector& inputs, std::vector&) { - mMode = static_cast(3.0f * inputs.at(mNumDefaultInputs + 0).Get() + 0.5f); - mScale = l::math::functions::sqrt(mNodeInputManager.GetValue(3)); + void GraphFilterChamberlain2pole::UpdateSignal(std::vector&, std::vector&) { + mMode = static_cast(3.0f * mNodeInputManager.GetValueNext(mNumDefaultInputs + 0) + 0.5f); + mScale = l::math::functions::sqrt(mNodeInputManager.GetValueNext(3)); mScaleFilter.SetConvergenceFactor().SetTarget(mScale).SnapAt(); } diff --git a/packages/nodegraph/source/common/operations/NodeGraphOpSignal.cpp b/packages/nodegraph/source/common/operations/NodeGraphOpSignal.cpp index a1fc7225..6dcfbc47 100644 --- a/packages/nodegraph/source/common/operations/NodeGraphOpSignal.cpp +++ b/packages/nodegraph/source/common/operations/NodeGraphOpSignal.cpp @@ -22,7 +22,7 @@ namespace l::nodegraph { mSamplesUntilUpdate = l::audio::BatchUpdate(mUpdateRate, mSamplesUntilUpdate, 0, numSamples, [&]() { - mUpdateRate = inputs.at(1).Get(); + mUpdateRate = mNodeInputManager.GetValueNext(1); mNodeInputManager.NodeUpdate(inputs, mUpdateRate); mSmooth = mNodeInputManager.GetValueNext(4); From b0976d0481a19e7582a707760b0b88516cc37551 Mon Sep 17 00:00:00 2001 From: lnd3 Date: Tue, 24 Sep 2024 13:07:09 +0200 Subject: [PATCH 36/42] Add output lod parameter to numeric operations so we can choose to use less than full precision and interpolate depending on what the application is. For example control parameters do not need sample level precision. --- .../include/nodegraph/core/NodeGraphBase.h | 1 + .../include/nodegraph/core/NodeGraphData.h | 2 +- .../nodegraph/operations/NodeGraphOpLogic.h | 39 +++++++--- .../nodegraph/operations/NodeGraphOpNumeric.h | 74 +++++++++---------- .../source/common/core/NodeGraphOutput.cpp | 2 +- 5 files changed, 63 insertions(+), 55 deletions(-) diff --git a/packages/nodegraph/include/nodegraph/core/NodeGraphBase.h b/packages/nodegraph/include/nodegraph/core/NodeGraphBase.h index 39fd3e70..50b824f7 100644 --- a/packages/nodegraph/include/nodegraph/core/NodeGraphBase.h +++ b/packages/nodegraph/include/nodegraph/core/NodeGraphBase.h @@ -245,6 +245,7 @@ namespace l::nodegraph { std::vector mCustom; }; + // Use this when the operation requires dynamic input parameters or when the input data can be sampled scarcily (not every sample etc) class NodeGraphOp2 : public NodeGraphOp { public: NodeGraphOp2(NodeGraphBase* node, std::string_view name) : diff --git a/packages/nodegraph/include/nodegraph/core/NodeGraphData.h b/packages/nodegraph/include/nodegraph/core/NodeGraphData.h index 04dd9d6f..d2aa9602 100644 --- a/packages/nodegraph/include/nodegraph/core/NodeGraphData.h +++ b/packages/nodegraph/include/nodegraph/core/NodeGraphData.h @@ -111,7 +111,7 @@ namespace l::nodegraph { }; /*********************************************************************************/ enum class InputTypeBase { - SAMPLED = 0, // todo: make it interpolate in smaller custom buffers + SAMPLED = 0, // interpolate in a buffer the size of a ProcessSubGraph(size) call. The actual size is defined by the lod factor of the output buffer. SAMPLED_RWA, // todo: add a smoothed sampled variant. Will replace interp_rwa and interp_rwa_ms as we will add a separate config for passing ticks or millis CONSTANT_ARRAY, // Same here, will be replaced CUSTOM_INTERP_TWEEN, // custom input vars should not be used at all diff --git a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpLogic.h b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpLogic.h index 7bc7785b..bd1846f7 100644 --- a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpLogic.h +++ b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpLogic.h @@ -36,10 +36,15 @@ namespace l::nodegraph { } virtual ~GraphLogicalAnd() = default; - virtual void Process(int32_t, std::vector& inputs, std::vector& outputs) override { - bool input1 = inputs.at(0).Get() != 0.0f; - bool input2 = inputs.at(1).Get() != 0.0f; - outputs.at(0).mOutput = (input1 && input2) ? 1.0f : 0.0f; + virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override { + auto input1 = inputs.at(0).GetIterator(numSamples); + auto input2 = inputs.at(1).GetIterator(numSamples); + auto output = outputs.at(0).GetIterator(); + for (int32_t i = 0; i < numSamples; i++) { + bool bool1 = (*input1++) != 0.0f; + bool bool2 = (*input2++) != 0.0f; + *output++ = (bool1 && bool2) ? 1.0f : 0.0f; + } } }; @@ -55,10 +60,15 @@ namespace l::nodegraph { } virtual ~GraphLogicalOr() = default; - virtual void Process(int32_t, std::vector& inputs, std::vector& outputs) override { - bool input1 = inputs.at(0).Get() != 0.0f; - bool input2 = inputs.at(1).Get() != 0.0f; - outputs.at(0).mOutput = (input1 || input2) ? 1.0f : 0.0f; + virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override { + auto input1 = inputs.at(0).GetIterator(numSamples); + auto input2 = inputs.at(1).GetIterator(numSamples); + auto output = outputs.at(0).GetIterator(); + for (int32_t i = 0; i < numSamples; i++) { + bool bool1 = (*input1++) != 0.0f; + bool bool2 = (*input2++) != 0.0f; + *output++ = (bool1 || bool2) ? 1.0f : 0.0f; + } } }; @@ -74,10 +84,15 @@ namespace l::nodegraph { } virtual ~GraphLogicalXor() = default; - virtual void Process(int32_t, std::vector& inputs, std::vector& outputs) override { - bool input1 = inputs.at(0).Get() != 0.0f; - bool input2 = inputs.at(1).Get() != 0.0f; - outputs.at(0).mOutput = (input1 ^ input2) ? 1.0f : 0.0f; + virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override { + auto input1 = inputs.at(0).GetIterator(numSamples); + auto input2 = inputs.at(1).GetIterator(numSamples); + auto output = outputs.at(0).GetIterator(); + for (int32_t i = 0; i < numSamples; i++) { + bool bool1 = (*input1++) != 0.0f; + bool bool2 = (*input2++) != 0.0f; + *output++ = (bool1 ^ bool2) ? 1.0f : 0.0f; + } } }; diff --git a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpNumeric.h b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpNumeric.h index 41cd599a..3acb53d1 100644 --- a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpNumeric.h +++ b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpNumeric.h @@ -30,17 +30,16 @@ namespace l::nodegraph { { AddInput("In 1"); AddInput("In 2"); + AddInput("Lod", 0.0f, 1, 0.0f, 8.0f); AddOutput("Out"); } virtual ~GraphNumericAdd() = default; virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override { auto input0 = inputs.at(0).GetIterator(numSamples); auto input1 = inputs.at(1).GetIterator(numSamples); - auto output = outputs.at(0).GetIterator(numSamples); - - if (numSamples > 1) { - ASSERT(numSamples == outputs.at(0).GetSize()); - } + auto lodExp = inputs.at(2).Get(); + auto lodFactor = l::math::functions::pow(2.0f, l::math::functions::round(lodExp)); + auto output = outputs.at(0).GetIterator(numSamples, lodFactor); for (int32_t i = 0; i < numSamples; i++) { *output++ = *input0++ + *input1++; @@ -56,6 +55,7 @@ namespace l::nodegraph { { AddInput("In 1"); AddInput("In 2"); + AddInput("Lod", 0.0f, 1, 0.0f, 8.0f); AddOutput("Out"); } @@ -63,11 +63,9 @@ namespace l::nodegraph { virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override { auto input0 = inputs.at(0).GetIterator(numSamples); auto input1 = inputs.at(1).GetIterator(numSamples); - auto output = outputs.at(0).GetIterator(numSamples); - - if (numSamples > 1) { - ASSERT(numSamples == outputs.at(0).GetSize()); - } + auto lodExp = inputs.at(2).Get(); + auto lodFactor = l::math::functions::pow(2.0f, l::math::functions::round(lodExp)); + auto output = outputs.at(0).GetIterator(numSamples, lodFactor); for (int32_t i = 0; i < numSamples; i++) { *output++ = *input0++ * *input1++; @@ -83,17 +81,16 @@ namespace l::nodegraph { { AddInput("In 1"); AddInput("In 2"); + AddInput("Lod", 0.0f, 1, 0.0f, 8.0f); AddOutput("Out"); } virtual ~GraphNumericSubtract() = default; virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override { auto input0 = inputs.at(0).GetIterator(numSamples); auto input1 = inputs.at(1).GetIterator(numSamples); - auto output = outputs.at(0).GetIterator(numSamples); - - if (numSamples > 1) { - ASSERT(numSamples == outputs.at(0).GetSize()); - } + auto lodExp = inputs.at(2).Get(); + auto lodFactor = l::math::functions::pow(2.0f, l::math::functions::round(lodExp)); + auto output = outputs.at(0).GetIterator(numSamples, lodFactor); for (int32_t i = 0; i < numSamples; i++) { *output++ = *input0++ - *input1++; @@ -108,17 +105,16 @@ namespace l::nodegraph { NodeGraphOp(node, "Negate") { AddInput("In"); + AddInput("Lod", 0.0f, 1, 0.0f, 8.0f); AddOutput("Out"); } virtual ~GraphNumericNegate() = default; virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override { auto input0 = inputs.at(0).GetIterator(numSamples); - auto output = outputs.at(0).GetIterator(numSamples); - - if (numSamples > 1) { - ASSERT(numSamples == outputs.at(0).GetSize()); - } + auto lodExp = inputs.at(1).Get(); + auto lodFactor = l::math::functions::pow(2.0f, l::math::functions::round(lodExp)); + auto output = outputs.at(0).GetIterator(numSamples, lodFactor); for (int32_t i = 0; i < numSamples; i++) { *output++ = - *input0++; @@ -133,17 +129,16 @@ namespace l::nodegraph { NodeGraphOp(node, "Integral") { AddInput("In", 0.0f, 1); + AddInput("Lod", 0.0f, 1, 0.0f, 8.0f); AddOutput("Out", 0.0f, 1); } virtual ~GraphNumericIntegral() = default; virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override { auto input0 = inputs.at(0).GetIterator(numSamples); - auto output = outputs.at(0).GetIterator(numSamples); - - if (numSamples > 1) { - ASSERT(numSamples == outputs.at(0).GetSize()); - } + auto lodExp = inputs.at(1).Get(); + auto lodFactor = l::math::functions::pow(2.0f, l::math::functions::round(lodExp)); + auto output = outputs.at(0).GetIterator(numSamples, lodFactor); for (int32_t i = 0; i < numSamples; i++) { mOutput += *input0++; @@ -163,6 +158,7 @@ namespace l::nodegraph { AddInput("In 1"); AddInput("In 2"); AddInput("In 3"); + AddInput("Lod", 0.0f, 1, 0.0f, 8.0f); AddOutput("Out"); } @@ -171,11 +167,9 @@ namespace l::nodegraph { auto input0 = inputs.at(0).GetIterator(numSamples); auto input1 = inputs.at(1).GetIterator(numSamples); auto input2 = inputs.at(2).GetIterator(numSamples); - auto output = outputs.at(0).GetIterator(numSamples); - - if (numSamples > 1) { - ASSERT(numSamples == outputs.at(0).GetSize()); - } + auto lodExp = inputs.at(3).Get(); + auto lodFactor = l::math::functions::pow(2.0f, l::math::functions::round(lodExp)); + auto output = outputs.at(0).GetIterator(numSamples, lodFactor); for (int32_t i = 0; i < numSamples; i++) { *output++ = *input0++ * *input1++ * *input2++; @@ -192,6 +186,7 @@ namespace l::nodegraph { AddInput("In 1"); AddInput("In 2"); AddInput("In 3"); + AddInput("Lod", 0.0f, 1, 0.0f, 8.0f); AddOutput("Out"); } @@ -200,11 +195,9 @@ namespace l::nodegraph { auto input0 = inputs.at(0).GetIterator(numSamples); auto input1 = inputs.at(1).GetIterator(numSamples); auto input2 = inputs.at(2).GetIterator(numSamples); - auto output = outputs.at(0).GetIterator(numSamples); - - if (numSamples > 1) { - ASSERT(numSamples == outputs.at(0).GetSize()); - } + auto lodExp = inputs.at(3).Get(); + auto lodFactor = l::math::functions::pow(2.0f, l::math::functions::round(lodExp)); + auto output = outputs.at(0).GetIterator(numSamples, lodFactor); for (int32_t i = 0; i < numSamples; i++) { *output++ = *input0++ * *input1++ + *input2++; @@ -219,6 +212,7 @@ namespace l::nodegraph { NodeGraphOp(node, "Round") { AddInput("In"); + AddInput("Lod", 0.0f, 1, 0.0f, 8.0f); AddOutput("Out"); } @@ -226,12 +220,10 @@ namespace l::nodegraph { virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override { outputs.at(0).mOutput = l::math::functions::round(inputs.at(0).Get()); - auto input0 = inputs.at(2).GetIterator(numSamples); - auto output = outputs.at(0).GetIterator(numSamples); - - if (numSamples > 1) { - ASSERT(numSamples == outputs.at(0).GetSize()); - } + auto input0 = inputs.at(0).GetIterator(numSamples); + auto lodExp = inputs.at(1).Get(); + auto lodFactor = l::math::functions::pow(2.0f, l::math::functions::round(lodExp)); + auto output = outputs.at(0).GetIterator(numSamples, lodFactor); for (int32_t i = 0; i < numSamples; i++) { *output++ = l::math::functions::round(*input0++); diff --git a/packages/nodegraph/source/common/core/NodeGraphOutput.cpp b/packages/nodegraph/source/common/core/NodeGraphOutput.cpp index 27762df8..744ff2f7 100644 --- a/packages/nodegraph/source/common/core/NodeGraphOutput.cpp +++ b/packages/nodegraph/source/common/core/NodeGraphOutput.cpp @@ -31,7 +31,7 @@ namespace l::nodegraph { } NodeDataIterator NodeGraphOutput::GetIterator(int32_t size, float lod) { - if (mOutputLod == 1.0f && lod > 1.0f) { + if (lod >= 1.0f && lod <= size) { mOutputLod = lod; } float stepPerIndex = size == 1 ? 0.0f : 1.0f / mOutputLod; From f282b4ae0ba74f64816fe6a65f7bfa1b2e611b61 Mon Sep 17 00:00:00 2001 From: lnd3 Date: Tue, 24 Sep 2024 15:15:48 +0200 Subject: [PATCH 37/42] Document. Optimize input meta data structures slightly. --- packages/audio/include/audio/AudioUtils.h | 16 ---------------- .../include/nodegraph/core/NodeGraphData.h | 15 +++++++-------- .../source/common/core/NodeGraphInput.cpp | 2 +- 3 files changed, 8 insertions(+), 25 deletions(-) diff --git a/packages/audio/include/audio/AudioUtils.h b/packages/audio/include/audio/AudioUtils.h index bcec4a50..100f2c5f 100644 --- a/packages/audio/include/audio/AudioUtils.h +++ b/packages/audio/include/audio/AudioUtils.h @@ -35,7 +35,6 @@ namespace l::audio { FilterRWA() : mSmooth(static_cast(0.005)), - mSmoothSkewed(mSmooth * mSmooth), mValue(static_cast(0)), mTargetValue(static_cast(0)), mRWAUpdateRate(static_cast(1.0)) @@ -52,19 +51,16 @@ namespace l::audio { FilterRWA& SetConvergenceInMs(T convergenceInMS, T limit = static_cast(0.0001), T sampleRate = static_cast(44100.0)) { mSmooth = GetRWAFactorFromMS(convergenceInMS, limit, mRWAUpdateRate, sampleRate); - mSmoothSkewed = GetRWAFactorFromMSSkewed(convergenceInMS, limit, mRWAUpdateRate, sampleRate); return *this; } FilterRWA& SetConvergenceFactor(T smooth = static_cast(0.005)) { mSmooth = smooth; - mSmoothSkewed = mSmooth * mSmooth; return *this; } FilterRWA& SetConvergenceInTicks(T ticks, T limit = static_cast(0.001)) { mSmooth = l::math::tween::GetRWAFactor(static_cast(ticks), limit); - mSmoothSkewed = mSmooth * mSmooth; return *this; } @@ -78,17 +74,6 @@ namespace l::audio { return *this; } - T NextSkewed() { - float delta = mTargetValue - mValue; - if (delta < 0) { - mValue += mSmooth * delta; - } - else { - mValue += mSmoothSkewed * delta; - } - return mValue; - } - T Next() { mValue += mSmooth * (mTargetValue - mValue); return mValue; @@ -100,7 +85,6 @@ namespace l::audio { protected: T mSmooth; - T mSmoothSkewed; T mValue; T mTargetValue; T mRWAUpdateRate; diff --git a/packages/nodegraph/include/nodegraph/core/NodeGraphData.h b/packages/nodegraph/include/nodegraph/core/NodeGraphData.h index d2aa9602..0fdc3371 100644 --- a/packages/nodegraph/include/nodegraph/core/NodeGraphData.h +++ b/packages/nodegraph/include/nodegraph/core/NodeGraphData.h @@ -82,9 +82,8 @@ namespace l::nodegraph { return 1.0f / mIncrement; } protected: - int32_t mSize = 0; - float mIndex = 0.0f; float* mData = nullptr; + float mIndex = 0.0f; float mIncrement = 0.0f; }; @@ -111,12 +110,12 @@ namespace l::nodegraph { }; /*********************************************************************************/ enum class InputTypeBase { - SAMPLED = 0, // interpolate in a buffer the size of a ProcessSubGraph(size) call. The actual size is defined by the lod factor of the output buffer. - SAMPLED_RWA, // todo: add a smoothed sampled variant. Will replace interp_rwa and interp_rwa_ms as we will add a separate config for passing ticks or millis - CONSTANT_ARRAY, // Same here, will be replaced - CUSTOM_INTERP_TWEEN, // custom input vars should not be used at all - CUSTOM_INTERP_TWEEN_MS, - CUSTOM_INTERP_RWA_MS + SAMPLED = 0, // interpolate in a buffer the size of a ProcessSubGraph(size) call. The actual size is defined by the lod factor of the source output buffer. + SAMPLED_RWA, // same as SAMPLED, but it also uses RWA on the output with a smoothing factor defined by the lod factor of the source output buffer + CONSTANT_ARRAY, // user defined array for custom usage + CUSTOM_INTERP_TWEEN, // custom input vars that tweens the input like a s curve + CUSTOM_INTERP_TWEEN_MS, // millisecond synchronized tweening + CUSTOM_INTERP_RWA_MS // custom input var for rwa smoothing synchronized to milliseconds }; union InputUnion { diff --git a/packages/nodegraph/source/common/core/NodeGraphInput.cpp b/packages/nodegraph/source/common/core/NodeGraphInput.cpp index e18e8f69..8a5b1e24 100644 --- a/packages/nodegraph/source/common/core/NodeGraphInput.cpp +++ b/packages/nodegraph/source/common/core/NodeGraphInput.cpp @@ -238,7 +238,7 @@ namespace l::nodegraph { mInput.mIterator = input.at(mInputIndex).GetIterator(numSamples); break; case InputTypeBase::SAMPLED_RWA: - mInput.mIteratorRwa = input.at(mInputIndex).GetIterator(numSamples); + mInput.mIteratorRwa = NodeDataIteratorRwa(input.at(mInputIndex).GetIterator(numSamples)); break; case InputTypeBase::CONSTANT_ARRAY: mInput.mIterator = input.at(mInputIndex).GetArrayIterator(); From 6ad9b3f08d80199d66a81b9ae64855313385b1bf Mon Sep 17 00:00:00 2001 From: lnd3 Date: Tue, 24 Sep 2024 16:29:22 +0200 Subject: [PATCH 38/42] Further reduce input meta data size keeping it at or below 32 bytes for all options. Overhaul filters and fix some issues. --- packages/math/include/math/MathTween.h | 1 - packages/math/source/common/MathTween.cpp | 8 +++----- packages/math/tests/common/MathTweenTest.cpp | 8 ++++---- .../include/nodegraph/operations/NodeGraphOpFilter.h | 2 +- .../source/common/operations/NodeGraphOpFilter.cpp | 3 ++- packages/nodegraph/tests/common/NodeGraphBatchingTest.cpp | 2 +- packages/nodegraph/tests/common/NodeGraphSchemaTest.cpp | 6 +++--- 7 files changed, 14 insertions(+), 16 deletions(-) diff --git a/packages/math/include/math/MathTween.h b/packages/math/include/math/MathTween.h index 1f6fca37..381e5da8 100644 --- a/packages/math/include/math/MathTween.h +++ b/packages/math/include/math/MathTween.h @@ -45,7 +45,6 @@ namespace l::math::tween { float Value(); protected: - float mUpdateRate = 0.0f; int32_t mUpdateCount = 1; float mDynamicSmoothAccuracy = 0.1f; float mDynamicSmooth = 0.25f; diff --git a/packages/math/source/common/MathTween.cpp b/packages/math/source/common/MathTween.cpp index d13c658c..626e341b 100644 --- a/packages/math/source/common/MathTween.cpp +++ b/packages/math/source/common/MathTween.cpp @@ -73,14 +73,11 @@ namespace l::math::tween { // update rate in audio processing, the number of samples between calls // we can use this to update expensive interpolation here void DynamicTween::Update(float updateRate) { - if (updateRate != mUpdateRate) { - mUpdateRate = l::math::functions::max(updateRate, 2.0f); - mDynamicSmooth = GetRWAFactor(static_cast(mUpdateRate), mDynamicSmoothAccuracy); - } + mDynamicSmooth = GetRWAFactor(static_cast(updateRate), mDynamicSmoothAccuracy); mTarget = mTargetValue; if (mCounter < mUpdateCount) { - float t = (mCounter + 0.5f * mUpdateRate) / static_cast(mUpdateCount - 0.5f * mUpdateRate); + float t = (mCounter + 0.5f) / static_cast(mUpdateCount - 0.5f); t = l::math::functions::clamp(t, 0.0f, 1.0f); mTarget = mTargetValuePrev + l::math::smooth::smoothPolyh3(t) * (mTargetValue - mTargetValuePrev); } @@ -95,4 +92,5 @@ namespace l::math::tween { float DynamicTween::Value() { return mValue; } + } diff --git a/packages/math/tests/common/MathTweenTest.cpp b/packages/math/tests/common/MathTweenTest.cpp index d42ec505..2286281b 100644 --- a/packages/math/tests/common/MathTweenTest.cpp +++ b/packages/math/tests/common/MathTweenTest.cpp @@ -38,7 +38,7 @@ TEST(MathTween, DynamicTween) { } TEST(MathTween, DynamicTweenVeryShortBatch) { - DynamicTween tween(0.1f); + DynamicTween tween(0.01f); tween.SetValue(1.0f); tween.SetTarget(0.0, 22); TEST_FUZZY(tween.Value(), 1.0f, 0.01f, ""); @@ -55,7 +55,7 @@ TEST(MathTween, DynamicTweenVeryShortBatch) { } TEST(MathTween, DynamicTweenShortBatch) { - DynamicTween tween(0.35f); + DynamicTween tween(0.135f); tween.SetValue(1.0f); tween.SetTarget(0.0, 102); TEST_FUZZY(tween.Value(), 1.0f, 0.01f, ""); @@ -72,7 +72,7 @@ TEST(MathTween, DynamicTweenShortBatch) { } TEST(MathTween, DynamicTweenLongBatch) { - int32_t updateRate = 100; + float updateRate = 100.0f; DynamicTween tween(0.35f); tween.SetValue(1.0f); tween.SetTarget(0.0, 1020); @@ -84,7 +84,7 @@ TEST(MathTween, DynamicTweenLongBatch) { auto value = tween.Next(); LOG(LogInfo) << "Tween (" << i << "): " << value; } - TEST_FUZZY(tween.Value(), 0.0f, 0.01f, ""); + TEST_FUZZY(tween.Value(), 0.0f, 0.02f, ""); return 0; } diff --git a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpFilter.h b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpFilter.h index db6c5db1..82bff617 100644 --- a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpFilter.h +++ b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpFilter.h @@ -102,7 +102,7 @@ namespace l::nodegraph { GraphFilterChamberlain2pole(NodeGraphBase* node) : GraphFilterBase(node, "Chamberlin two-pole") { - AddInput("Mode"); + mNodeInputManager.AddInput(InputTypeBase::SAMPLED, AddInput("Mode")); mState.resize(4); } virtual ~GraphFilterChamberlain2pole() = default; diff --git a/packages/nodegraph/source/common/operations/NodeGraphOpFilter.cpp b/packages/nodegraph/source/common/operations/NodeGraphOpFilter.cpp index a900be4f..e2d00d73 100644 --- a/packages/nodegraph/source/common/operations/NodeGraphOpFilter.cpp +++ b/packages/nodegraph/source/common/operations/NodeGraphOpFilter.cpp @@ -112,8 +112,9 @@ namespace l::nodegraph { float GraphFilterChamberlain2pole::ProcessSignal(float input, float cutoff, float resonance) { float inputValueInbetween = (mInputValuePrev + input) * 0.5f; float scale = mScaleFilter.Next(); + resonance *= 0.99f * (cutoff * 0.15f + 0.85f); // adjust resonance slightly down at low frequencies to avoid oscillation cutoff *= 0.5f; - resonance = 1.0f - resonance; + resonance = (1.0f - resonance); for (int32_t oversample = 0; oversample < 2; oversample++) { mState.at(0) = mState.at(0) + cutoff * mState.at(2); mState.at(1) = scale * (oversample == 0 ? inputValueInbetween : input) - mState.at(0) - resonance * mState.at(2); diff --git a/packages/nodegraph/tests/common/NodeGraphBatchingTest.cpp b/packages/nodegraph/tests/common/NodeGraphBatchingTest.cpp index b676245d..f29c9e97 100644 --- a/packages/nodegraph/tests/common/NodeGraphBatchingTest.cpp +++ b/packages/nodegraph/tests/common/NodeGraphBatchingTest.cpp @@ -36,7 +36,7 @@ TEST(NodeGraphBatching, Simple) { auto output = &group.GetOutput(0, 8); - TEST_FUZZY(0.034771f, output[7], 0.0001f, ""); + TEST_FUZZY(0.012347f, output[7], 0.0001f, ""); return 0; } diff --git a/packages/nodegraph/tests/common/NodeGraphSchemaTest.cpp b/packages/nodegraph/tests/common/NodeGraphSchemaTest.cpp index 0ce45b9d..d1829b4d 100644 --- a/packages/nodegraph/tests/common/NodeGraphSchemaTest.cpp +++ b/packages/nodegraph/tests/common/NodeGraphSchemaTest.cpp @@ -109,7 +109,7 @@ TEST(NodeGraph, FilterLowpass) { //LOG(LogInfo) << nodeLowpass.GetOutput(0); } - TEST_FUZZY(nodeLowpass.GetOutput(0), -0.201134, 0.0001f, ""); + TEST_FUZZY(nodeLowpass.GetOutput(0), -0.287209, 0.0001f, ""); return 0; } @@ -173,8 +173,8 @@ TEST(NodeGraph, GraphGroups) { float output1 = group2.GetOutput(0); float output2 = group2.GetOutput(1); - TEST_FUZZY(output1, 0.149999559f, 0.00001, ""); - TEST_FUZZY(output2, 0.0999997109f, 0.00001, ""); + TEST_FUZZY(output1, 0.130579203f, 0.00001, ""); + TEST_FUZZY(output2, 0.0870528072f, 0.00001, ""); return 0; } From 7ff4444a2579584f0887504eb0ec3a284cee4bb6 Mon Sep 17 00:00:00 2001 From: lnd3 Date: Tue, 24 Sep 2024 21:18:53 +0200 Subject: [PATCH 39/42] Rename uiStorage. --- .../include/rendering/ui/UIContainer.h | 14 +++++----- .../include/rendering/ui/UICreator.h | 2 +- .../include/rendering/ui/UINodeEditor.h | 8 +++--- .../include/rendering/ui/UIVisitors.h | 8 +++--- .../source/common/ui/UIContainer.cpp | 26 +++++++++---------- .../rendering/source/common/ui/UICreator.cpp | 24 ++++++++--------- .../rendering/source/common/ui/UIVisitors.cpp | 6 ++--- 7 files changed, 44 insertions(+), 44 deletions(-) diff --git a/packages/rendering/include/rendering/ui/UIContainer.h b/packages/rendering/include/rendering/ui/UIContainer.h index 584af8b2..b49909af 100644 --- a/packages/rendering/include/rendering/ui/UIContainer.h +++ b/packages/rendering/include/rendering/ui/UIContainer.h @@ -413,10 +413,10 @@ namespace l::ui { UISplitMode mSplitMode; }; - class UIStorage { + class UIManager { public: - UIStorage() = default; - ~UIStorage() = default; + UIManager() = default; + ~UIManager() = default; UIHandle Add(std::unique_ptr container); void Remove(const UIHandle& handle); @@ -426,9 +426,9 @@ namespace l::ui { int32_t mIdCounter = 1; }; - UIHandle CreateContainer(UIStorage& uiStorage, uint32_t flags, UIRenderType renderType = UIRenderType::Rect, UIAlignH alignH = UIAlignH::Left, UIAlignV alignV = UIAlignV::Top, UILayoutH layoutH = UILayoutH::Fixed, UILayoutV layoutV = UILayoutV::Fixed); - UIHandle CreateSplit(UIStorage& uiStorage, uint32_t flags, UIRenderType renderType, UISplitMode splitMode = UISplitMode::AppendV, UILayoutH layoutH = UILayoutH::Fixed, UILayoutV layoutV = UILayoutV::Fixed); - void DeleteContainer(UIStorage& uiStorage, UIHandle handle); - void DeleteContainer(UIStorage& uiStorage, UIContainer* container); + UIHandle CreateContainer(UIManager& uiManager, uint32_t flags, UIRenderType renderType = UIRenderType::Rect, UIAlignH alignH = UIAlignH::Left, UIAlignV alignV = UIAlignV::Top, UILayoutH layoutH = UILayoutH::Fixed, UILayoutV layoutV = UILayoutV::Fixed); + UIHandle CreateSplit(UIManager& uiManager, uint32_t flags, UIRenderType renderType, UISplitMode splitMode = UISplitMode::AppendV, UILayoutH layoutH = UILayoutH::Fixed, UILayoutV layoutV = UILayoutV::Fixed); + void DeleteContainer(UIManager& uiManager, UIHandle handle); + void DeleteContainer(UIManager& uiManager, UIContainer* container); } diff --git a/packages/rendering/include/rendering/ui/UICreator.h b/packages/rendering/include/rendering/ui/UICreator.h index 038e91e3..08445cfd 100644 --- a/packages/rendering/include/rendering/ui/UICreator.h +++ b/packages/rendering/include/rendering/ui/UICreator.h @@ -19,5 +19,5 @@ namespace l::ui { - UIHandle CreateUINode(UIStorage& uiStorage, l::nodegraph::NodeGraphBase& node, ImVec2 p); + UIHandle CreateUINode(UIManager& uiManager, l::nodegraph::NodeGraphBase& node, ImVec2 p); } diff --git a/packages/rendering/include/rendering/ui/UINodeEditor.h b/packages/rendering/include/rendering/ui/UINodeEditor.h index 673052b5..592a85d0 100644 --- a/packages/rendering/include/rendering/ui/UINodeEditor.h +++ b/packages/rendering/include/rendering/ui/UINodeEditor.h @@ -18,8 +18,8 @@ namespace l::ui { class UINodeEditor : public UIBase { public: - UINodeEditor(std::string_view editorName) : mUIWindow(editorName), mLinkIOVisitor(mUIStorage), mSelectVisitor(mUIStorage) { - mUIRoot = CreateContainer(mUIStorage, l::ui::UIContainer_DragFlag | l::ui::UIContainer_ZoomFlag); + UINodeEditor(std::string_view editorName) : mUIWindow(editorName), mLinkIOVisitor(mUIManager), mSelectVisitor(mUIManager) { + mUIRoot = CreateContainer(mUIManager, l::ui::UIContainer_DragFlag | l::ui::UIContainer_ZoomFlag); mUIWindow.SetContentWindow([&]() { ImGui::PushItemWidth(400); @@ -57,7 +57,7 @@ namespace l::ui { auto nodeId = mNGSchema->NewNode(it.GetId()); auto node = mNGSchema->GetNode(nodeId); if (node != nullptr) { - auto uiNode = l::ui::CreateUINode(mUIStorage, *node, p); + auto uiNode = l::ui::CreateUINode(mUIManager, *node, p); mUIRoot->Add(uiNode); } } @@ -119,7 +119,7 @@ namespace l::ui { protected: UIWindow mUIWindow; - UIStorage mUIStorage; + UIManager mUIManager; UIHandle mUIRoot; InputState mUIInput; diff --git a/packages/rendering/include/rendering/ui/UIVisitors.h b/packages/rendering/include/rendering/ui/UIVisitors.h index f25b11fe..f98697c3 100644 --- a/packages/rendering/include/rendering/ui/UIVisitors.h +++ b/packages/rendering/include/rendering/ui/UIVisitors.h @@ -52,7 +52,7 @@ namespace l::ui { class UISelect : public UIVisitor { public: - UISelect(UIStorage& uiStorage) : mUIStorage(uiStorage) {} + UISelect(UIManager& uiManager) : mUIManager(uiManager) {} virtual bool Visit(UIContainer& container, const InputState& input); @@ -61,7 +61,7 @@ namespace l::ui { } protected: std::unordered_set mSelectedContainers; - UIStorage& mUIStorage; + UIManager& mUIManager; l::nodegraph::NodeGraphSchema* mNGSchema = nullptr; }; @@ -102,7 +102,7 @@ namespace l::ui { public: using HandlerFunctionType = bool(int32_t, int32_t, int32_t, int32_t, bool); - UILinkIO(UIStorage& uiStorage) : mUIStorage(uiStorage) {} + UILinkIO(UIManager& uiManager) : mUIManager(uiManager) {} ~UILinkIO() = default; virtual bool Active(UIContainer& container, const InputState& input); @@ -131,7 +131,7 @@ namespace l::ui { protected: bool mDragging = false; UIHandle mLinkContainer; - UIStorage& mUIStorage; + UIManager& mUIManager; l::nodegraph::NodeGraphSchema* mNGSchema = nullptr; }; diff --git a/packages/rendering/source/common/ui/UIContainer.cpp b/packages/rendering/source/common/ui/UIContainer.cpp index c86698b1..4e83d1d1 100644 --- a/packages/rendering/source/common/ui/UIContainer.cpp +++ b/packages/rendering/source/common/ui/UIContainer.cpp @@ -280,7 +280,7 @@ namespace l::ui { return false; } - UIHandle UIStorage::Add(std::unique_ptr container) { + UIHandle UIManager::Add(std::unique_ptr container) { auto id = mIdCounter++; auto stringId = container->GetStringId(); container->SetId(id); @@ -288,56 +288,56 @@ namespace l::ui { return UIHandle{ id, stringId, mContainers.at(id).get() }; } - void UIStorage::Remove(const UIHandle& handle) { + void UIManager::Remove(const UIHandle& handle) { if (handle.IsValid()) { mContainers.erase(handle.GetId()); } } - void UIStorage::Remove(UIContainer* container) { + void UIManager::Remove(UIContainer* container) { if (container != nullptr) { mContainers.erase(container->GetId()); } } - UIHandle CreateContainer(UIStorage& uiStorage, uint32_t flags, UIRenderType renderType, UIAlignH alignH, UIAlignV alignV, UILayoutH layoutH, UILayoutV layoutV) { + UIHandle CreateContainer(UIManager& uiManager, uint32_t flags, UIRenderType renderType, UIAlignH alignH, UIAlignV alignV, UILayoutH layoutH, UILayoutV layoutV) { std::unique_ptr container = std::make_unique(flags, renderType, alignH, alignV, layoutH, layoutV); auto stringId = CreateUniqueStringId(); container->SetStringId(stringId); - return uiStorage.Add(std::move(container)); + return uiManager.Add(std::move(container)); } - UIHandle CreateSplit(UIStorage& uiStorage, uint32_t flags, UIRenderType renderType, UISplitMode splitMode, UILayoutH layoutH, UILayoutV layoutV) { + UIHandle CreateSplit(UIManager& uiManager, uint32_t flags, UIRenderType renderType, UISplitMode splitMode, UILayoutH layoutH, UILayoutV layoutV) { std::unique_ptr container = std::make_unique(flags, renderType, splitMode, layoutH, layoutV); auto stringId = CreateUniqueStringId(); container->SetStringId(stringId); - return uiStorage.Add(std::move(container)); + return uiManager.Add(std::move(container)); } - void DeleteContainer(UIStorage& uiStorage, UIHandle handle) { + void DeleteContainer(UIManager& uiManager, UIHandle handle) { if (handle.IsValid()) { ASSERT(handle.Get()->GetParent() != nullptr); handle.Get()->GetParent()->Remove(handle); handle->ForEachChild([&](UIContainer* container) { - uiStorage.Remove(container); + uiManager.Remove(container); }); - uiStorage.Remove(handle); + uiManager.Remove(handle); } } - void DeleteContainer(UIStorage& uiStorage, UIContainer* container) { + void DeleteContainer(UIManager& uiManager, UIContainer* container) { if (container != nullptr) { ASSERT(container->GetParent() != nullptr); container->GetParent()->Remove(container); container->ForEachChild([&](UIContainer* c) { - uiStorage.Remove(c); + uiManager.Remove(c); }); - uiStorage.Remove(container); + uiManager.Remove(container); } } } diff --git a/packages/rendering/source/common/ui/UICreator.cpp b/packages/rendering/source/common/ui/UICreator.cpp index 6dd38d10..ffdc0ed0 100644 --- a/packages/rendering/source/common/ui/UICreator.cpp +++ b/packages/rendering/source/common/ui/UICreator.cpp @@ -5,14 +5,14 @@ namespace l::ui { - UIHandle CreateUINode(UIStorage& uiStorage, l::nodegraph::NodeGraphBase& node, ImVec2 p) { + UIHandle CreateUINode(UIManager& uiManager, l::nodegraph::NodeGraphBase& node, ImVec2 p) { auto numInputChannels = node.GetNumInputs(); auto numOutputChannels = node.GetNumOutputs(); auto numRows = numInputChannels > numOutputChannels ? numInputChannels : numOutputChannels; - auto node4 = CreateSplit(uiStorage, l::ui::UIContainer_ResizeFlag | l::ui::UIContainer_MoveFlag | l::ui::UIContainer_DrawFlag | UIContainer_SelectFlag, l::ui::UIRenderType::RectFilled, l::ui::UISplitMode::AppendV); + auto node4 = CreateSplit(uiManager, l::ui::UIContainer_ResizeFlag | l::ui::UIContainer_MoveFlag | l::ui::UIContainer_DrawFlag | UIContainer_SelectFlag, l::ui::UIRenderType::RectFilled, l::ui::UISplitMode::AppendV); node4->SetColor(ImVec4(0.15f, 0.15f, 0.15f, 1.0f)); node4->SetPosition(p); node4->GetContainerArea().mMargin = 0.0f; @@ -22,12 +22,12 @@ namespace l::ui { float ioSize = 4.0f; float ioOffsetV = 1.6f; - auto row0 = CreateContainer(uiStorage, l::ui::UIContainer_DrawFlag, l::ui::UIRenderType::RectFilled, l::ui::UIAlignH::Left, l::ui::UIAlignV::Top, l::ui::UILayoutH::Parent, l::ui::UILayoutV::Fixed); + auto row0 = CreateContainer(uiManager, l::ui::UIContainer_DrawFlag, l::ui::UIRenderType::RectFilled, l::ui::UIAlignH::Left, l::ui::UIAlignV::Top, l::ui::UILayoutH::Parent, l::ui::UILayoutV::Fixed); row0->SetSize(ImVec2(1.0f, 18.0f)); row0->GetContainerArea().mMargin = 2.0f; node4->Add(row0); { - auto connector1Text = CreateContainer(uiStorage, l::ui::UIContainer_DrawFlag, l::ui::UIRenderType::Text, l::ui::UIAlignH::Center, l::ui::UIAlignV::Middle); + auto connector1Text = CreateContainer(uiManager, l::ui::UIContainer_DrawFlag, l::ui::UIRenderType::Text, l::ui::UIAlignH::Center, l::ui::UIAlignV::Middle); connector1Text->SetDisplayName(node.GetName()); auto textSize = ImGui::CalcTextSize(node.GetName().data()); sizeEstimate.x = sizeEstimate.x < textSize.x ? textSize.x : sizeEstimate.x; @@ -35,7 +35,7 @@ namespace l::ui { row0->Add(connector1Text); } for (int8_t i = 0; i < numInputChannels || i < numOutputChannels || i < numInputChannels || i < numOutputChannels; i++) { - auto row = CreateContainer(uiStorage, 0, l::ui::UIRenderType::Rect, l::ui::UIAlignH::Left, l::ui::UIAlignV::Top, l::ui::UILayoutH::Parent, l::ui::UILayoutV::Fixed); + auto row = CreateContainer(uiManager, 0, l::ui::UIRenderType::Rect, l::ui::UIAlignH::Left, l::ui::UIAlignV::Top, l::ui::UILayoutH::Parent, l::ui::UILayoutV::Fixed); row->GetContainerArea().mMargin = ioSize; node4->Add(row); { @@ -44,7 +44,7 @@ namespace l::ui { float estimatedWidth = 0.0f; if (showsInput && !node.IsDataConstant(i)) { - auto in = CreateContainer(uiStorage, l::ui::UIContainer_InputFlag | l::ui::UIContainer_DrawFlag, l::ui::UIRenderType::CircleFilled, l::ui::UIAlignH::Left); + auto in = CreateContainer(uiManager, l::ui::UIContainer_InputFlag | l::ui::UIContainer_DrawFlag, l::ui::UIRenderType::CircleFilled, l::ui::UIAlignH::Left); in->SetPosition(ImVec2(-ioSize, ioSize * ioOffsetV)); in->SetSize(ImVec2(ioSize, ioSize)); in->GetContainerArea().mMargin = 0.0f; @@ -52,7 +52,7 @@ namespace l::ui { in->SetChannelId(i); estimatedWidth += ioSize; row->Add(in); - auto inText = CreateContainer(uiStorage, l::ui::UIContainer_DrawFlag, l::ui::UIRenderType::Text, l::ui::UIAlignH::Left); + auto inText = CreateContainer(uiManager, l::ui::UIContainer_DrawFlag, l::ui::UIRenderType::Text, l::ui::UIAlignH::Left); inText->SetPosition(ImVec2(0.0f, 0.0f)); inText->SetDisplayName(node.GetInputName(i)); estimatedWidth += ImGui::CalcTextSize(node.GetInputName(i).data()).x; @@ -60,7 +60,7 @@ namespace l::ui { } if (node.IsDataVisible(i)) { - auto inText = CreateContainer(uiStorage, (node.IsDataEditable(i) ? l::ui::UIContainer_EditFlag : 0) | l::ui::UIContainer_DrawFlag, l::ui::UIRenderType::NodeOutputValue, l::ui::UIAlignH::Left); + auto inText = CreateContainer(uiManager, (node.IsDataEditable(i) ? l::ui::UIContainer_EditFlag : 0) | l::ui::UIContainer_DrawFlag, l::ui::UIRenderType::NodeOutputValue, l::ui::UIAlignH::Left); inText->SetPosition(ImVec2(estimatedWidth, 0.0f)); inText->SetSize(ImVec2(10 * 7, 14.0f)); inText->SetNodeId(node.GetId()); @@ -70,7 +70,7 @@ namespace l::ui { } if (showsOutput) { - auto out = CreateContainer(uiStorage, l::ui::UIContainer_OutputFlag | l::ui::UIContainer_DrawFlag, l::ui::UIRenderType::CircleFilled, l::ui::UIAlignH::Right); + auto out = CreateContainer(uiManager, l::ui::UIContainer_OutputFlag | l::ui::UIContainer_DrawFlag, l::ui::UIRenderType::CircleFilled, l::ui::UIAlignH::Right); out->SetPosition(ImVec2(ioSize * 2.0f, ioSize * ioOffsetV)); out->SetSize(ImVec2(ioSize, ioSize)); out->GetContainerArea().mMargin = 0.0f; @@ -78,7 +78,7 @@ namespace l::ui { out->SetChannelId(i); estimatedWidth += ioSize; row->Add(out); - auto outText = CreateContainer(uiStorage, l::ui::UIContainer_DrawFlag, l::ui::UIRenderType::Text, l::ui::UIAlignH::Right); + auto outText = CreateContainer(uiManager, l::ui::UIContainer_DrawFlag, l::ui::UIRenderType::Text, l::ui::UIAlignH::Right); outText->SetPosition(ImVec2(0.0f, 0.0f)); outText->SetDisplayName(node.GetOutputName(i)); estimatedWidth += ImGui::CalcTextSize(node.GetOutputName(i).data()).x; @@ -89,13 +89,13 @@ namespace l::ui { } if (node.GetOutputType() == l::nodegraph::OutputType::ExternalVisualOutput) { - auto row = CreateContainer(uiStorage, l::ui::UIContainer_DrawFlag, l::ui::UIRenderType::Rect, l::ui::UIAlignH::Left, l::ui::UIAlignV::Bottom, l::ui::UILayoutH::Parent, l::ui::UILayoutV::Parent); + auto row = CreateContainer(uiManager, l::ui::UIContainer_DrawFlag, l::ui::UIRenderType::Rect, l::ui::UIAlignH::Left, l::ui::UIAlignV::Bottom, l::ui::UILayoutH::Parent, l::ui::UILayoutV::Parent); row->SetPosition(ImVec2(0.0f, 0.0f)); row->GetContainerArea().mMargin = ioSize; node4->Add(row); float estimatedWidth = 0.0f; - auto plot = CreateContainer(uiStorage, l::ui::UIContainer_DrawFlag, l::ui::UIRenderType::NodeOutputGraph, l::ui::UIAlignH::Center, l::ui::UIAlignV::Bottom, l::ui::UILayoutH::Parent, l::ui::UILayoutV::Parent); + auto plot = CreateContainer(uiManager, l::ui::UIContainer_DrawFlag, l::ui::UIRenderType::NodeOutputGraph, l::ui::UIAlignH::Center, l::ui::UIAlignV::Bottom, l::ui::UILayoutH::Parent, l::ui::UILayoutV::Parent); plot->SetPosition(ImVec2(estimatedWidth, 0.0f)); plot->SetSize(ImVec2(100, 100)); plot->SetNodeId(node.GetId()); diff --git a/packages/rendering/source/common/ui/UIVisitors.cpp b/packages/rendering/source/common/ui/UIVisitors.cpp index 1ca5d65e..1efaf270 100644 --- a/packages/rendering/source/common/ui/UIVisitors.cpp +++ b/packages/rendering/source/common/ui/UIVisitors.cpp @@ -182,7 +182,7 @@ namespace l::ui { if (mNGSchema != nullptr) { mNGSchema->RemoveNode(it->GetNodeId()); } - DeleteContainer(mUIStorage, it); + DeleteContainer(mUIManager, it); } mSelectedContainers.clear(); return true; @@ -435,7 +435,7 @@ namespace l::ui { ImVec2 pT = layoutArea.Transform(pCenter); if (OverlapCircle(input.mCurPos, pT, 2.0f * size.x * layoutArea.mScale)) { mDragging = true; - mLinkContainer = CreateContainer(mUIStorage, UIContainer_LinkFlag | UIContainer_DrawFlag, UIRenderType::LinkH); + mLinkContainer = CreateContainer(mUIManager, UIContainer_LinkFlag | UIContainer_DrawFlag, UIRenderType::LinkH); container.Add(mLinkContainer); return true; } @@ -489,7 +489,7 @@ namespace l::ui { mLinkContainer.Reset(); } else { - DeleteContainer(mUIStorage, mLinkContainer.Get()); + DeleteContainer(mUIManager, mLinkContainer.Get()); mDragging = false; mLinkContainer.Reset(); return true; From 2727ba1f52fcdab9a46107eee8234b61ead07885 Mon Sep 17 00:00:00 2001 From: lnd3 Date: Wed, 25 Sep 2024 14:41:15 +0200 Subject: [PATCH 40/42] More structure. Forward declare. Add ng test for tweens. --- packages/audio/include/audio/PortAudio.h | 2 +- .../tests/common/NodeGraphDataTest.cpp | 58 ++++++++++++++++++- .../tests/common/NodeGraphSchemaTest.cpp | 2 +- .../include/rendering/ui/UIVisitors.h | 22 ++----- .../rendering/source/common/ui/UIVisitors.cpp | 18 ++++++ 5 files changed, 82 insertions(+), 20 deletions(-) diff --git a/packages/audio/include/audio/PortAudio.h b/packages/audio/include/audio/PortAudio.h index 6f864af9..dcba8846 100644 --- a/packages/audio/include/audio/PortAudio.h +++ b/packages/audio/include/audio/PortAudio.h @@ -73,7 +73,7 @@ namespace l::audio { AudioStream() = default; ~AudioStream() = default; - bool OpenStream(int32_t dacBufferFrames, float latency = 0.0f, BufferingMode mode = BufferingMode::DOUBLE_BUFFERING, ChannelMode channel = ChannelMode::STEREO); + bool OpenStream(int32_t dacFramesPerBufferPart, float latencyMs = 0.0f, BufferingMode mode = BufferingMode::DOUBLE_BUFFERING, ChannelMode channel = ChannelMode::STEREO); bool StartStream(); std::vector& GetWriteBuffer(); bool CanWrite(); diff --git a/packages/nodegraph/tests/common/NodeGraphDataTest.cpp b/packages/nodegraph/tests/common/NodeGraphDataTest.cpp index f89eac7d..0a115213 100644 --- a/packages/nodegraph/tests/common/NodeGraphDataTest.cpp +++ b/packages/nodegraph/tests/common/NodeGraphDataTest.cpp @@ -41,7 +41,7 @@ class TestOp : public NodeGraphOp2 { -TEST(NodeGraphData, Setup) { +TEST(NodeGraphData, Sampler) { NodeGraph node0; NodeGraph node1; @@ -66,3 +66,59 @@ TEST(NodeGraphData, Setup) { } +class TestOp2 : public NodeGraphOp2 { +public: + TestOp2(NodeGraphBase* node) : + NodeGraphOp2(node, "TestOp") + { + AddInput2(InputTypeBase::CUSTOM_INTERP_TWEEN, "In 0", 0.0f, 1, 0.0f, 1.0f); + AddInput2(InputTypeBase::CUSTOM_INTERP_TWEEN_MS, "In 1", 0.0f, 1, 0.0f, 1.0f); + AddOutput("Out 0"); + AddOutput("Out 1"); + } + + virtual ~TestOp2() = default; + virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override { + mNodeInputManager.BatchUpdate(inputs, numSamples); + auto out0 = outputs.at(0).GetIterator(numSamples, 4.0f); + auto out1 = outputs.at(1).GetIterator(numSamples, 4.0f); + + mNodeInputManager.SetDuration(0, 24.0f, 0.1); + mNodeInputManager.SetDuration(1, 0.5f, 0.1); + + for (int i = 0; i < numSamples; i++) { + float value0 = mNodeInputManager.GetValueNext(0); + float value1 = mNodeInputManager.GetValueNext(1); + *out0++ = value0; + *out1++ = value1; + + LOG(LogInfo) << "Node input(" << i << "): " << value0 << ", " << value1; + } + } +protected: +}; + +TEST(NodeGraphData, Tweening) { + + NodeGraph node0; + NodeGraph node1; + + node0.SetInput(0, 0.0f, 2); + node0.SetInput(1, 0.0f, 2); + node1.SetInput(0, node0, 0); + node1.SetInput(1, node0, 1); + + { + auto input0 = node0.GetInputOf(0).GetIterator(32); + auto input1 = node0.GetInputOf(1).GetIterator(32); + for (int i = 0; i < 32; i++) { + *input0++ = static_cast(i > 24 ? 24 : i); + *input1++ = static_cast(i > 24 ? 24 : i); + } + } + + node1.ProcessSubGraph(32); + + return 0; +} + diff --git a/packages/nodegraph/tests/common/NodeGraphSchemaTest.cpp b/packages/nodegraph/tests/common/NodeGraphSchemaTest.cpp index d1829b4d..dcfaf494 100644 --- a/packages/nodegraph/tests/common/NodeGraphSchemaTest.cpp +++ b/packages/nodegraph/tests/common/NodeGraphSchemaTest.cpp @@ -179,7 +179,7 @@ TEST(NodeGraph, GraphGroups) { return 0; } -TEST(NodeGraph, SchemaBasic) { +TEST(NodeGraph, SchemaAllNodes) { NodeGraphSchema ng; diff --git a/packages/rendering/include/rendering/ui/UIVisitors.h b/packages/rendering/include/rendering/ui/UIVisitors.h index f98697c3..2a9df045 100644 --- a/packages/rendering/include/rendering/ui/UIVisitors.h +++ b/packages/rendering/include/rendering/ui/UIVisitors.h @@ -3,11 +3,13 @@ #include "rendering/ui/UIContainer.h" #include "rendering/ui/UIWindow.h" -#include "nodegraph/NodeGraphSchema.h" - #include #include +namespace l::nodegraph { + class NodeGraphSchema; +} + namespace l::ui { class UIUpdate : public UIVisitor { @@ -108,21 +110,7 @@ namespace l::ui { virtual bool Active(UIContainer& container, const InputState& input); virtual bool Visit(UIContainer& container, const InputState& input); - bool LinkHandler(int32_t linkInputId, int32_t linkOutputId, int32_t inputChannel, int32_t outputChannel, bool connected) { - if (mNGSchema == nullptr) { - return false; - } - - auto inputNode = mNGSchema->GetNode(linkInputId); - if (inputNode == nullptr) { - return false; - } - if (connected) { - auto outputNode = mNGSchema->GetNode(linkOutputId); - return outputNode != nullptr && inputNode->SetInput(static_cast(inputChannel), *outputNode, static_cast(outputChannel)); - } - return inputNode->ClearInput(static_cast(inputChannel)); - } + bool LinkHandler(int32_t linkInputId, int32_t linkOutputId, int32_t inputChannel, int32_t outputChannel, bool connected); void SetNGSchema(l::nodegraph::NodeGraphSchema* ngSchema) { mNGSchema = ngSchema; diff --git a/packages/rendering/source/common/ui/UIVisitors.cpp b/packages/rendering/source/common/ui/UIVisitors.cpp index 1efaf270..2bc45312 100644 --- a/packages/rendering/source/common/ui/UIVisitors.cpp +++ b/packages/rendering/source/common/ui/UIVisitors.cpp @@ -1,5 +1,7 @@ #include "rendering/ui/UIVisitors.h" #include "hid/KeyboardPiano.h" +#include "nodegraph/NodeGraphSchema.h" + namespace l::ui { @@ -499,4 +501,20 @@ namespace l::ui { return false; } + bool UILinkIO::LinkHandler(int32_t linkInputId, int32_t linkOutputId, int32_t inputChannel, int32_t outputChannel, bool connected) { + if (mNGSchema == nullptr) { + return false; + } + + auto inputNode = mNGSchema->GetNode(linkInputId); + if (inputNode == nullptr) { + return false; + } + if (connected) { + auto outputNode = mNGSchema->GetNode(linkOutputId); + return outputNode != nullptr && inputNode->SetInput(static_cast(inputChannel), *outputNode, static_cast(outputChannel)); + } + return inputNode->ClearInput(static_cast(inputChannel)); + } + } From 8d5ec6bc9ebccf488b368db912e34c15d6f3b46d Mon Sep 17 00:00:00 2001 From: lnd3 Date: Wed, 25 Sep 2024 15:20:47 +0200 Subject: [PATCH 41/42] Name changes. --- .../include/nodegraph/core/NodeGraphBase.h | 24 ++-- .../include/nodegraph/core/NodeGraphData.h | 2 +- .../include/nodegraph/core/NodeGraphInput.h | 8 +- .../nodegraph/operations/NodeGraphOpControl.h | 56 ++++---- .../nodegraph/operations/NodeGraphOpFilter.h | 14 +- .../nodegraph/operations/NodeGraphOpOutput.h | 6 +- .../nodegraph/operations/NodeGraphOpSignal.h | 22 +-- .../source/common/core/NodeGraphBase.cpp | 40 +++--- .../source/common/core/NodeGraphInput.cpp | 134 +++++++++--------- .../common/operations/NodeGraphOpControl.cpp | 104 +++++++------- .../common/operations/NodeGraphOpFilter.cpp | 12 +- .../common/operations/NodeGraphOpOutput.cpp | 4 +- .../common/operations/NodeGraphOpSignal.cpp | 30 ++-- .../tests/common/NodeGraphDataTest.cpp | 24 ++-- 14 files changed, 240 insertions(+), 240 deletions(-) diff --git a/packages/nodegraph/include/nodegraph/core/NodeGraphBase.h b/packages/nodegraph/include/nodegraph/core/NodeGraphBase.h index 50b824f7..431bc00a 100644 --- a/packages/nodegraph/include/nodegraph/core/NodeGraphBase.h +++ b/packages/nodegraph/include/nodegraph/core/NodeGraphBase.h @@ -216,18 +216,18 @@ namespace l::nodegraph { }; - class NodeInputManager { + class InputManager { public: const int32_t gCustomIndexBase = 100; - NodeInputManager(NodeGraphOp& nodeGraphOperation) : + InputManager(NodeGraphOp& nodeGraphOperation) : mNodeGraphOperation(nodeGraphOperation) { } - ~NodeInputManager() = default; + ~InputManager() = default; - int32_t AddInput(InputTypeBase type, int32_t inputIndex = -1); - int32_t AddCustom(InputTypeBase type); + int32_t AddInput(InputIterationType type, int32_t inputIndex = -1); + int32_t AddCustom(InputIterationType type); void BatchUpdate(std::vector& inputs, int32_t numSamples); void NodeUpdate(std::vector& inputs, float updateRate); float GetValueNext(int32_t inputIndex); @@ -241,8 +241,8 @@ namespace l::nodegraph { protected: NodeGraphOp& mNodeGraphOperation; - std::vector mInputs; - std::vector mCustom; + std::vector mInputs; + std::vector mCustom; }; // Use this when the operation requires dynamic input parameters or when the input data can be sampled scarcily (not every sample etc) @@ -250,7 +250,7 @@ namespace l::nodegraph { public: NodeGraphOp2(NodeGraphBase* node, std::string_view name) : NodeGraphOp(node, name), - mNodeInputManager(*this) { + mInputManager(*this) { } ~NodeGraphOp2() { @@ -260,7 +260,7 @@ namespace l::nodegraph { virtual void Tick(int32_t, float) override; int32_t AddInput2( - InputTypeBase type, + InputIterationType type, std::string_view name, float defaultValue = 0.0f, int32_t size = 1, @@ -270,7 +270,7 @@ namespace l::nodegraph { bool editable = true); int32_t AddConstant2( - InputTypeBase type, + InputIterationType type, std::string_view name, float defaultValue = 0.0f, int32_t size = 1, @@ -280,10 +280,10 @@ namespace l::nodegraph { bool editable = true); int32_t AddCustom2( - InputTypeBase type); + InputIterationType type); protected: - NodeInputManager mNodeInputManager; + InputManager mInputManager; float mStepsUntilUpdate = 0.0f; float mUpdateRate = 16.0f; }; diff --git a/packages/nodegraph/include/nodegraph/core/NodeGraphData.h b/packages/nodegraph/include/nodegraph/core/NodeGraphData.h index 0fdc3371..f2c6c46b 100644 --- a/packages/nodegraph/include/nodegraph/core/NodeGraphData.h +++ b/packages/nodegraph/include/nodegraph/core/NodeGraphData.h @@ -109,7 +109,7 @@ namespace l::nodegraph { l::audio::FilterRWAFloat mRwa; }; /*********************************************************************************/ - enum class InputTypeBase { + enum class InputIterationType { SAMPLED = 0, // interpolate in a buffer the size of a ProcessSubGraph(size) call. The actual size is defined by the lod factor of the source output buffer. SAMPLED_RWA, // same as SAMPLED, but it also uses RWA on the output with a smoothing factor defined by the lod factor of the source output buffer CONSTANT_ARRAY, // user defined array for custom usage diff --git a/packages/nodegraph/include/nodegraph/core/NodeGraphInput.h b/packages/nodegraph/include/nodegraph/core/NodeGraphInput.h index 0e3840d7..4127699c 100644 --- a/packages/nodegraph/include/nodegraph/core/NodeGraphInput.h +++ b/packages/nodegraph/include/nodegraph/core/NodeGraphInput.h @@ -42,14 +42,14 @@ namespace l::nodegraph { /*********************************************************************************/ - class NodeGraphInputAccessor { + class InputAccessor { public: - NodeGraphInputAccessor(InputTypeBase type, int32_t inputIndex) : + InputAccessor(InputIterationType type, int32_t inputIndex) : mType(type), mInputIndex(inputIndex) { } - ~NodeGraphInputAccessor() = default; + ~InputAccessor() = default; void SetUpdateRate(float updateRate); void SetDuration(float ms, float limit = 0.001f); @@ -64,7 +64,7 @@ namespace l::nodegraph { void NodeUpdate(std::vector& input, float updateRate); protected: - InputTypeBase mType; + InputIterationType mType; int32_t mInputIndex = 0; InputUnion mInput; }; diff --git a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpControl.h b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpControl.h index 859b94dd..3bb22cfa 100644 --- a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpControl.h +++ b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpControl.h @@ -30,16 +30,16 @@ namespace l::nodegraph { GraphControlBase(NodeGraphBase* node, std::string_view name) : NodeGraphOp(node, name), - mNodeInputManager(*this) + mInputManager(*this) { - mNodeInputManager.AddInput(InputTypeBase::SAMPLED, AddInput("Sync", 0.0f, 1, 0.0f, 1.0f)); - mNodeInputManager.AddInput(InputTypeBase::SAMPLED, AddInput("Rate", 256.0f, 1, 1.0f, 2048.0f)); - mNodeInputManager.AddInput(InputTypeBase::SAMPLED, AddInput("Velocity", 0.5f, 1, 0.0f, 1.0f)); - mNodeInputManager.AddInput(InputTypeBase::SAMPLED, AddInput("Fade", 0.1f, 1, 0.0001f, 1.0f)); - mNodeInputManager.AddInput(InputTypeBase::SAMPLED, AddInput("Attack", 50.0f, 1, 1.0f, 10000.0f)); - mNodeInputManager.AddInput(InputTypeBase::SAMPLED, AddInput("Release", 50.0f, 1, 1.0f, 10000.0f)); - mNodeInputManager.AddCustom(InputTypeBase::CUSTOM_INTERP_RWA_MS); // 100 - mNodeInputManager.AddCustom(InputTypeBase::CUSTOM_INTERP_RWA_MS); // 101 + mInputManager.AddInput(InputIterationType::SAMPLED, AddInput("Sync", 0.0f, 1, 0.0f, 1.0f)); + mInputManager.AddInput(InputIterationType::SAMPLED, AddInput("Rate", 256.0f, 1, 1.0f, 2048.0f)); + mInputManager.AddInput(InputIterationType::SAMPLED, AddInput("Velocity", 0.5f, 1, 0.0f, 1.0f)); + mInputManager.AddInput(InputIterationType::SAMPLED, AddInput("Fade", 0.1f, 1, 0.0001f, 1.0f)); + mInputManager.AddInput(InputIterationType::SAMPLED, AddInput("Attack", 50.0f, 1, 1.0f, 10000.0f)); + mInputManager.AddInput(InputIterationType::SAMPLED, AddInput("Release", 50.0f, 1, 1.0f, 10000.0f)); + mInputManager.AddCustom(InputIterationType::CUSTOM_INTERP_RWA_MS); // 100 + mInputManager.AddCustom(InputIterationType::CUSTOM_INTERP_RWA_MS); // 101 AddOutput("Freq"); AddOutput("Volume"); @@ -49,10 +49,10 @@ namespace l::nodegraph { virtual ~GraphControlBase() = default; virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; - virtual void UpdateSignal(NodeInputManager& inputManager) = 0; - virtual std::pair ProcessSignal(NodeInputManager& inputManager) = 0; + virtual void UpdateSignal(InputManager& inputManager) = 0; + virtual std::pair ProcessSignal(InputManager& inputManager) = 0; protected: - NodeInputManager mNodeInputManager; + InputManager mInputManager; float mUpdateRate = 256.0f; float mDeltaTime = 0.0f; @@ -65,13 +65,13 @@ namespace l::nodegraph { GraphControlEnvelope(NodeGraphBase* node) : GraphControlBase(node, "Envelope") { - mNodeInputManager.AddInput(InputTypeBase::SAMPLED_RWA, AddInput("Freq")); + mInputManager.AddInput(InputIterationType::SAMPLED_RWA, AddInput("Freq")); mEnvelope = 0.0f; } virtual ~GraphControlEnvelope() = default; - virtual void UpdateSignal(NodeInputManager& inputManager) override; - virtual std::pair ProcessSignal(NodeInputManager& inputManager) override; + virtual void UpdateSignal(InputManager& inputManager) override; + virtual std::pair ProcessSignal(InputManager& inputManager) override; protected: bool mNoteOn = false; int32_t mFrameCount = 0; @@ -94,18 +94,18 @@ namespace l::nodegraph { const static int32_t gPolyphony = 12; GraphControlArpeggio(NodeGraphBase* node) : NodeGraphOp(node, "Arpeggio"), - mNodeInputManager(*this) + mInputManager(*this) { - mNodeInputManager.AddInput(InputTypeBase::SAMPLED, AddInput("Sync", 0.0f, 1, 0.0f, 1.0f)); - mNodeInputManager.AddInput(InputTypeBase::SAMPLED_RWA, AddInput("Bpm", 60.0f, 1, 1.0f, 1000.0f)); - mNodeInputManager.AddInput(InputTypeBase::SAMPLED, AddInput("Velocity", 0.5f, 1, 0.0f, 1.0f)); - mNodeInputManager.AddInput(InputTypeBase::SAMPLED, AddInput("Fade", 1.0f, 1, 0.0001f, 1.0f)); - mNodeInputManager.AddInput(InputTypeBase::SAMPLED, AddInput("Attack", 5.0f, 1, 1.0f, 10000.0f)); - mNodeInputManager.AddInput(InputTypeBase::SAMPLED, AddInput("Release", 20.0f, 1, 1.0f, 10000.0f)); - mNodeInputManager.AddInput(InputTypeBase::CONSTANT_ARRAY, AddInput("Note On", l::audio::gNoNote_f, gPolyphony, -499.0, 500.0)); - mNodeInputManager.AddInput(InputTypeBase::CONSTANT_ARRAY, AddInput("Note Off", l::audio::gNoNote_f, gPolyphony, -499.0, 500.0)); - mNodeInputManager.AddCustom(InputTypeBase::CUSTOM_INTERP_RWA_MS); // 100 - mNodeInputManager.AddCustom(InputTypeBase::CUSTOM_INTERP_RWA_MS); // 101 + mInputManager.AddInput(InputIterationType::SAMPLED, AddInput("Sync", 0.0f, 1, 0.0f, 1.0f)); + mInputManager.AddInput(InputIterationType::SAMPLED_RWA, AddInput("Bpm", 60.0f, 1, 1.0f, 1000.0f)); + mInputManager.AddInput(InputIterationType::SAMPLED, AddInput("Velocity", 0.5f, 1, 0.0f, 1.0f)); + mInputManager.AddInput(InputIterationType::SAMPLED, AddInput("Fade", 1.0f, 1, 0.0001f, 1.0f)); + mInputManager.AddInput(InputIterationType::SAMPLED, AddInput("Attack", 5.0f, 1, 1.0f, 10000.0f)); + mInputManager.AddInput(InputIterationType::SAMPLED, AddInput("Release", 20.0f, 1, 1.0f, 10000.0f)); + mInputManager.AddInput(InputIterationType::CONSTANT_ARRAY, AddInput("Note On", l::audio::gNoNote_f, gPolyphony, -499.0, 500.0)); + mInputManager.AddInput(InputIterationType::CONSTANT_ARRAY, AddInput("Note Off", l::audio::gNoNote_f, gPolyphony, -499.0, 500.0)); + mInputManager.AddCustom(InputIterationType::CUSTOM_INTERP_RWA_MS); // 100 + mInputManager.AddCustom(InputIterationType::CUSTOM_INTERP_RWA_MS); // 101 AddOutput("Freq"); AddOutput("Volume"); @@ -115,9 +115,9 @@ namespace l::nodegraph { virtual ~GraphControlArpeggio() = default; virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; - void UpdateSignal(NodeInputManager& inputManager); + void UpdateSignal(InputManager& inputManager); protected: - NodeInputManager mNodeInputManager; + InputManager mInputManager; float mSamplesUntilUpdate = 0.0f; float mUpdateRate = 256.0f; diff --git a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpFilter.h b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpFilter.h index 82bff617..e937b0a1 100644 --- a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpFilter.h +++ b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpFilter.h @@ -31,16 +31,16 @@ namespace l::nodegraph { GraphFilterBase(NodeGraphBase* node, std::string_view name) : NodeGraphOp(node, name), - mNodeInputManager(*this) + mInputManager(*this) { mDefaultInStrings.clear(); mDefaultOutStrings.clear(); mDefaultInData.clear(); - mNodeInputManager.AddInput(InputTypeBase::SAMPLED, AddInput("Sync", 0.0f)); - mNodeInputManager.AddInput(InputTypeBase::SAMPLED, AddInput("In")); - mNodeInputManager.AddInput(InputTypeBase::SAMPLED_RWA, AddInput("Cutoff", 1.0f)); - mNodeInputManager.AddInput(InputTypeBase::SAMPLED_RWA, AddInput("Resonance", 0.0f)); + mInputManager.AddInput(InputIterationType::SAMPLED, AddInput("Sync", 0.0f)); + mInputManager.AddInput(InputIterationType::SAMPLED, AddInput("In")); + mInputManager.AddInput(InputIterationType::SAMPLED_RWA, AddInput("Cutoff", 1.0f)); + mInputManager.AddInput(InputIterationType::SAMPLED_RWA, AddInput("Resonance", 0.0f)); AddOutput("Out", 0.0f); } @@ -54,7 +54,7 @@ namespace l::nodegraph { virtual void UpdateSignal(std::vector&, std::vector&) {}; virtual float ProcessSignal(float input, float cutoff, float resonance) = 0; protected: - NodeInputManager mNodeInputManager; + InputManager mInputManager; float mSync = 0.0f; float mSamplesUntilUpdate = 0.0f; @@ -102,7 +102,7 @@ namespace l::nodegraph { GraphFilterChamberlain2pole(NodeGraphBase* node) : GraphFilterBase(node, "Chamberlin two-pole") { - mNodeInputManager.AddInput(InputTypeBase::SAMPLED, AddInput("Mode")); + mInputManager.AddInput(InputIterationType::SAMPLED, AddInput("Mode")); mState.resize(4); } virtual ~GraphFilterChamberlain2pole() = default; diff --git a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpOutput.h b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpOutput.h index eca34a8a..0268db89 100644 --- a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpOutput.h +++ b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpOutput.h @@ -75,17 +75,17 @@ namespace l::nodegraph { public: GraphOutputPlot(NodeGraphBase* node, int32_t plotSamples) : NodeGraphOp(node, "Plot"), - mNodeInputManager(*this), + mInputManager(*this), mPlotSamples(plotSamples) { - mNodeInputManager.AddInput(InputTypeBase::SAMPLED, AddInput("Plot", 0.0f, 1, -1.0f, 1.0f)); + mInputManager.AddInput(InputIterationType::SAMPLED, AddInput("Plot", 0.0f, 1, -1.0f, 1.0f)); AddOutput("Plot", 0.0f, mPlotSamples); } virtual ~GraphOutputPlot() = default; virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; protected: - NodeInputManager mNodeInputManager; + InputManager mInputManager; int32_t mPlotSamples = 50; int32_t mCurIndex = 0; diff --git a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpSignal.h b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpSignal.h index 95ea00c9..31db6719 100644 --- a/packages/nodegraph/include/nodegraph/operations/NodeGraphOpSignal.h +++ b/packages/nodegraph/include/nodegraph/operations/NodeGraphOpSignal.h @@ -30,13 +30,13 @@ namespace l::nodegraph { GraphSignalBase(NodeGraphBase* node, std::string_view name) : NodeGraphOp(node, name), - mNodeInputManager(*this) + mInputManager(*this) { - mNodeInputManager.AddInput(InputTypeBase::SAMPLED, AddInput("Sync", 0.0f, 1, 0.0f, 1.0f)); - mNodeInputManager.AddInput(InputTypeBase::SAMPLED, AddInput("Rate", 256.0f, 1, 1.0f, 2048.0f)); - mNodeInputManager.AddInput(InputTypeBase::SAMPLED_RWA, AddInput("Freq", 0.0f, 1, 0.0f, 22050.0f)); - mNodeInputManager.AddInput(InputTypeBase::SAMPLED_RWA, AddInput("Volume", 0.0f, 1, 0.0f, 5.0f)); - mNodeInputManager.AddInput(InputTypeBase::SAMPLED, AddInput("Smooth", 1.0f, 1, 0.0f, 1.0f)); + mInputManager.AddInput(InputIterationType::SAMPLED, AddInput("Sync", 0.0f, 1, 0.0f, 1.0f)); + mInputManager.AddInput(InputIterationType::SAMPLED, AddInput("Rate", 256.0f, 1, 1.0f, 2048.0f)); + mInputManager.AddInput(InputIterationType::SAMPLED_RWA, AddInput("Freq", 0.0f, 1, 0.0f, 22050.0f)); + mInputManager.AddInput(InputIterationType::SAMPLED_RWA, AddInput("Volume", 0.0f, 1, 0.0f, 5.0f)); + mInputManager.AddInput(InputIterationType::SAMPLED, AddInput("Smooth", 1.0f, 1, 0.0f, 1.0f)); AddOutput("Out", 0.0f, 2); @@ -57,7 +57,7 @@ namespace l::nodegraph { virtual void UpdateSignal(std::vector&, std::vector&) {}; virtual float ProcessSignal(float deltaTime, float freq) = 0; protected: - NodeInputManager mNodeInputManager; + InputManager mInputManager; float mReset = 0.0f; float mFreq = 0.0f; @@ -76,8 +76,8 @@ namespace l::nodegraph { GraphSignalSine2(NodeGraphBase* node) : GraphSignalBase(node, "Sine 2") { - mNodeInputManager.AddInput(InputTypeBase::SAMPLED_RWA, AddInput("Fmod", 0.0f, 1, 0.0f, 1.0f)); - mNodeInputManager.AddInput(InputTypeBase::SAMPLED_RWA, AddInput("Phase", 0.0f, 1, 0.0f, 1.0f)); + mInputManager.AddInput(InputIterationType::SAMPLED_RWA, AddInput("Fmod", 0.0f, 1, 0.0f, 1.0f)); + mInputManager.AddInput(InputIterationType::SAMPLED_RWA, AddInput("Phase", 0.0f, 1, 0.0f, 1.0f)); } virtual ~GraphSignalSine2() = default; @@ -119,8 +119,8 @@ namespace l::nodegraph { GraphSignalSaw2(NodeGraphBase* node) : GraphSignalBase(node, "Saw 2") { - mNodeInputManager.AddInput(InputTypeBase::SAMPLED_RWA, AddInput("Attenuation", 0.0f, 1, 0.0f, 1.0f)); - mNodeInputManager.AddInput(InputTypeBase::SAMPLED_RWA, AddInput("Cutoff", 0.0f, 1, 0.0f, 1.0f)); + mInputManager.AddInput(InputIterationType::SAMPLED_RWA, AddInput("Attenuation", 0.0f, 1, 0.0f, 1.0f)); + mInputManager.AddInput(InputIterationType::SAMPLED_RWA, AddInput("Cutoff", 0.0f, 1, 0.0f, 1.0f)); } virtual ~GraphSignalSaw2() = default; diff --git a/packages/nodegraph/source/common/core/NodeGraphBase.cpp b/packages/nodegraph/source/common/core/NodeGraphBase.cpp index 40cbc9cb..730375f7 100644 --- a/packages/nodegraph/source/common/core/NodeGraphBase.cpp +++ b/packages/nodegraph/source/common/core/NodeGraphBase.cpp @@ -380,21 +380,21 @@ namespace l::nodegraph { /**********************************************************************************/ - int32_t NodeInputManager::AddInput(InputTypeBase type, int32_t inputIndex) { + int32_t InputManager::AddInput(InputIterationType type, int32_t inputIndex) { inputIndex = static_cast(mInputs.size()); - mInputs.emplace_back(NodeGraphInputAccessor{ type, inputIndex }); + mInputs.emplace_back(InputAccessor{ type, inputIndex }); mInputs.back().SetValue(mNodeGraphOperation.GetDefaultData(static_cast(inputIndex))); mInputs.back().SetTarget(mNodeGraphOperation.GetDefaultData(static_cast(inputIndex))); return inputIndex; } - int32_t NodeInputManager::AddCustom(InputTypeBase type) { + int32_t InputManager::AddCustom(InputIterationType type) { auto inputIndex = static_cast(mCustom.size()); - mCustom.emplace_back(NodeGraphInputAccessor{ type, inputIndex }); + mCustom.emplace_back(InputAccessor{ type, inputIndex }); return inputIndex + gCustomIndexBase; } - void NodeInputManager::BatchUpdate(std::vector& inputs, int32_t numSamples) { + void InputManager::BatchUpdate(std::vector& inputs, int32_t numSamples) { for (auto& input : mInputs) { input.BatchUpdate(inputs, numSamples); } @@ -403,7 +403,7 @@ namespace l::nodegraph { } } - void NodeInputManager::NodeUpdate(std::vector& inputs, float updateRate) { + void InputManager::NodeUpdate(std::vector& inputs, float updateRate) { for (auto& input : mInputs) { input.NodeUpdate(inputs, updateRate); } @@ -412,35 +412,35 @@ namespace l::nodegraph { } } - float NodeInputManager::GetValueNext(int32_t inputIndex) { + float InputManager::GetValueNext(int32_t inputIndex) { if (inputIndex < gCustomIndexBase) { return mInputs.at(inputIndex).GetValueNext(); } return mCustom.at(inputIndex - gCustomIndexBase).GetValueNext(); } - float NodeInputManager::GetValue(int32_t inputIndex) { + float InputManager::GetValue(int32_t inputIndex) { if (inputIndex < gCustomIndexBase) { return mInputs.at(inputIndex).GetValue(); } return mCustom.at(inputIndex - gCustomIndexBase).GetValue(); } - float NodeInputManager::GetArrayValue(int32_t inputIndex, int32_t arrayIndex) { + float InputManager::GetArrayValue(int32_t inputIndex, int32_t arrayIndex) { if (inputIndex < gCustomIndexBase) { return mInputs.at(inputIndex).GetArrayValue(arrayIndex); } return mCustom.at(inputIndex - gCustomIndexBase).GetArrayValue(arrayIndex); } - float* NodeInputManager::GetArray(int32_t inputIndex) { + float* InputManager::GetArray(int32_t inputIndex) { if (inputIndex < gCustomIndexBase) { return mInputs.at(inputIndex).GetArray(); } return mCustom.at(inputIndex - gCustomIndexBase).GetArray(); } - void NodeInputManager::SetUpdateRate(int32_t inputIndex, float updateRate) { + void InputManager::SetUpdateRate(int32_t inputIndex, float updateRate) { if (inputIndex < gCustomIndexBase) { mInputs.at(inputIndex).SetUpdateRate(updateRate); return; @@ -448,7 +448,7 @@ namespace l::nodegraph { mCustom.at(inputIndex - gCustomIndexBase).SetUpdateRate(updateRate); } - void NodeInputManager::SetDuration(int32_t inputIndex, float value, float limit) { + void InputManager::SetDuration(int32_t inputIndex, float value, float limit) { if (inputIndex < gCustomIndexBase) { mInputs.at(inputIndex).SetDuration(value, limit); return; @@ -456,7 +456,7 @@ namespace l::nodegraph { mCustom.at(inputIndex - gCustomIndexBase).SetDuration(value, limit); } - void NodeInputManager::SetTarget(int32_t inputIndex, float value) { + void InputManager::SetTarget(int32_t inputIndex, float value) { if (inputIndex < gCustomIndexBase) { mInputs.at(inputIndex).SetTarget(value); return; @@ -464,7 +464,7 @@ namespace l::nodegraph { mCustom.at(inputIndex - gCustomIndexBase).SetTarget(value); } - void NodeInputManager::SetValue(int32_t inputIndex, float value) { + void InputManager::SetValue(int32_t inputIndex, float value) { if (inputIndex < gCustomIndexBase) { mInputs.at(inputIndex).SetValue(value); return; @@ -479,7 +479,7 @@ namespace l::nodegraph { } int32_t NodeGraphOp2::AddInput2( - InputTypeBase type, + InputIterationType type, std::string_view name, float defaultValue, int32_t size, @@ -487,11 +487,11 @@ namespace l::nodegraph { float boundMax, bool visible, bool editable) { - return mNodeInputManager.AddInput(type, NodeGraphOp::AddInput(name, defaultValue, size, boundMin, boundMax, visible, editable)); + return mInputManager.AddInput(type, NodeGraphOp::AddInput(name, defaultValue, size, boundMin, boundMax, visible, editable)); } int32_t NodeGraphOp2::AddConstant2( - InputTypeBase type, + InputIterationType type, std::string_view name, float defaultValue, int32_t size, @@ -499,11 +499,11 @@ namespace l::nodegraph { float boundMax, bool visible, bool editable) { - return mNodeInputManager.AddInput(type, NodeGraphOp::AddConstant(name, defaultValue, size, boundMin, boundMax, visible, editable)); + return mInputManager.AddInput(type, NodeGraphOp::AddConstant(name, defaultValue, size, boundMin, boundMax, visible, editable)); } - int32_t NodeGraphOp2::AddCustom2(InputTypeBase type) { - return mNodeInputManager.AddCustom(type); + int32_t NodeGraphOp2::AddCustom2(InputIterationType type) { + return mInputManager.AddCustom(type); } } \ No newline at end of file diff --git a/packages/nodegraph/source/common/core/NodeGraphInput.cpp b/packages/nodegraph/source/common/core/NodeGraphInput.cpp index 8a5b1e24..62d6c9cd 100644 --- a/packages/nodegraph/source/common/core/NodeGraphInput.cpp +++ b/packages/nodegraph/source/common/core/NodeGraphInput.cpp @@ -101,162 +101,162 @@ namespace l::nodegraph { /*********************************************************************************/ - void NodeGraphInputAccessor::SetUpdateRate(float updateRate) { + void InputAccessor::SetUpdateRate(float updateRate) { switch (mType) { - case InputTypeBase::CUSTOM_INTERP_TWEEN: - case InputTypeBase::CUSTOM_INTERP_TWEEN_MS: + case InputIterationType::CUSTOM_INTERP_TWEEN: + case InputIterationType::CUSTOM_INTERP_TWEEN_MS: mInput.mTween.Update(updateRate); break; - case InputTypeBase::CUSTOM_INTERP_RWA_MS: // must set it manually + case InputIterationType::CUSTOM_INTERP_RWA_MS: // must set it manually mInput.mFilterRWA.SetRWAUpdateRate(updateRate); break; - case InputTypeBase::SAMPLED: - case InputTypeBase::CONSTANT_ARRAY: - case InputTypeBase::SAMPLED_RWA: + case InputIterationType::SAMPLED: + case InputIterationType::CONSTANT_ARRAY: + case InputIterationType::SAMPLED_RWA: break; } } - void NodeGraphInputAccessor::SetDuration(int32_t ticks) { + void InputAccessor::SetDuration(int32_t ticks) { switch (mType) { - case InputTypeBase::CUSTOM_INTERP_RWA_MS: + case InputIterationType::CUSTOM_INTERP_RWA_MS: mInput.mFilterRWA.SetConvergenceInTicks(static_cast(ticks)); break; - case InputTypeBase::CUSTOM_INTERP_TWEEN: - case InputTypeBase::CUSTOM_INTERP_TWEEN_MS: + case InputIterationType::CUSTOM_INTERP_TWEEN: + case InputIterationType::CUSTOM_INTERP_TWEEN_MS: mInput.mTween.SetTweenLength(ticks); break; - case InputTypeBase::SAMPLED: - case InputTypeBase::SAMPLED_RWA: - case InputTypeBase::CONSTANT_ARRAY: + case InputIterationType::SAMPLED: + case InputIterationType::SAMPLED_RWA: + case InputIterationType::CONSTANT_ARRAY: break; } } - void NodeGraphInputAccessor::SetDuration(float ms, float limit) { + void InputAccessor::SetDuration(float ms, float limit) { switch (mType) { - case InputTypeBase::CUSTOM_INTERP_RWA_MS: + case InputIterationType::CUSTOM_INTERP_RWA_MS: mInput.mFilterRWA.SetConvergenceInMs(ms, limit); break; - case InputTypeBase::CUSTOM_INTERP_TWEEN: - case InputTypeBase::CUSTOM_INTERP_TWEEN_MS: + case InputIterationType::CUSTOM_INTERP_TWEEN: + case InputIterationType::CUSTOM_INTERP_TWEEN_MS: mInput.mTween.SetTweenLength(l::audio::GetAudioTicksFromMS(ms)); break; - case InputTypeBase::SAMPLED: - case InputTypeBase::SAMPLED_RWA: - case InputTypeBase::CONSTANT_ARRAY: + case InputIterationType::SAMPLED: + case InputIterationType::SAMPLED_RWA: + case InputIterationType::CONSTANT_ARRAY: break; } } - void NodeGraphInputAccessor::SetTarget(float value) { + void InputAccessor::SetTarget(float value) { switch (mType) { - case InputTypeBase::CUSTOM_INTERP_RWA_MS: + case InputIterationType::CUSTOM_INTERP_RWA_MS: mInput.mFilterRWA.SetTarget(value); break; - case InputTypeBase::CUSTOM_INTERP_TWEEN: - case InputTypeBase::CUSTOM_INTERP_TWEEN_MS: + case InputIterationType::CUSTOM_INTERP_TWEEN: + case InputIterationType::CUSTOM_INTERP_TWEEN_MS: mInput.mTween.SetTarget(value); break; - case InputTypeBase::SAMPLED: - case InputTypeBase::SAMPLED_RWA: - case InputTypeBase::CONSTANT_ARRAY: + case InputIterationType::SAMPLED: + case InputIterationType::SAMPLED_RWA: + case InputIterationType::CONSTANT_ARRAY: break; } } - void NodeGraphInputAccessor::SetValue(float value) { + void InputAccessor::SetValue(float value) { switch (mType) { - case InputTypeBase::CUSTOM_INTERP_RWA_MS: + case InputIterationType::CUSTOM_INTERP_RWA_MS: mInput.mFilterRWA.Value() = value; break; - case InputTypeBase::CUSTOM_INTERP_TWEEN: - case InputTypeBase::CUSTOM_INTERP_TWEEN_MS: + case InputIterationType::CUSTOM_INTERP_TWEEN: + case InputIterationType::CUSTOM_INTERP_TWEEN_MS: mInput.mTween.Reset(value); break; - case InputTypeBase::SAMPLED: - case InputTypeBase::SAMPLED_RWA: - case InputTypeBase::CONSTANT_ARRAY: + case InputIterationType::SAMPLED: + case InputIterationType::SAMPLED_RWA: + case InputIterationType::CONSTANT_ARRAY: break; } } - float NodeGraphInputAccessor::GetValueNext() { + float InputAccessor::GetValueNext() { switch (mType) { - case InputTypeBase::CUSTOM_INTERP_RWA_MS: + case InputIterationType::CUSTOM_INTERP_RWA_MS: return mInput.mFilterRWA.Next(); - case InputTypeBase::CUSTOM_INTERP_TWEEN: - case InputTypeBase::CUSTOM_INTERP_TWEEN_MS: + case InputIterationType::CUSTOM_INTERP_TWEEN: + case InputIterationType::CUSTOM_INTERP_TWEEN_MS: return mInput.mTween.Next(); - case InputTypeBase::SAMPLED: - case InputTypeBase::CONSTANT_ARRAY: + case InputIterationType::SAMPLED: + case InputIterationType::CONSTANT_ARRAY: return *mInput.mIterator++; - case InputTypeBase::SAMPLED_RWA: + case InputIterationType::SAMPLED_RWA: return mInput.mIteratorRwa++; } return 0.0f; } - float NodeGraphInputAccessor::GetValue() { + float InputAccessor::GetValue() { switch (mType) { - case InputTypeBase::CUSTOM_INTERP_RWA_MS: + case InputIterationType::CUSTOM_INTERP_RWA_MS: return mInput.mFilterRWA.Value(); - case InputTypeBase::CUSTOM_INTERP_TWEEN: - case InputTypeBase::CUSTOM_INTERP_TWEEN_MS: + case InputIterationType::CUSTOM_INTERP_TWEEN: + case InputIterationType::CUSTOM_INTERP_TWEEN_MS: return mInput.mTween.Value(); - case InputTypeBase::SAMPLED: - case InputTypeBase::CONSTANT_ARRAY: + case InputIterationType::SAMPLED: + case InputIterationType::CONSTANT_ARRAY: return *mInput.mIterator; - case InputTypeBase::SAMPLED_RWA: + case InputIterationType::SAMPLED_RWA: return *mInput.mIteratorRwa; } return 0.0f; } - float NodeGraphInputAccessor::GetArrayValue(int32_t index) { - if (mType == InputTypeBase::CONSTANT_ARRAY) { + float InputAccessor::GetArrayValue(int32_t index) { + if (mType == InputIterationType::CONSTANT_ARRAY) { return mInput.mIterator[index]; } return 0.0f; } - float* NodeGraphInputAccessor::GetArray() { - if (mType == InputTypeBase::CONSTANT_ARRAY) { + float* InputAccessor::GetArray() { + if (mType == InputIterationType::CONSTANT_ARRAY) { return mInput.mIterator.data(); } return nullptr; } // run on each new batch call to setup input iterators for buffered data - void NodeGraphInputAccessor::BatchUpdate(std::vector& input, int32_t numSamples) { + void InputAccessor::BatchUpdate(std::vector& input, int32_t numSamples) { switch (mType) { - case InputTypeBase::CUSTOM_INTERP_RWA_MS: - case InputTypeBase::CUSTOM_INTERP_TWEEN: - case InputTypeBase::CUSTOM_INTERP_TWEEN_MS: + case InputIterationType::CUSTOM_INTERP_RWA_MS: + case InputIterationType::CUSTOM_INTERP_TWEEN: + case InputIterationType::CUSTOM_INTERP_TWEEN_MS: break; - case InputTypeBase::SAMPLED: + case InputIterationType::SAMPLED: mInput.mIterator = input.at(mInputIndex).GetIterator(numSamples); break; - case InputTypeBase::SAMPLED_RWA: + case InputIterationType::SAMPLED_RWA: mInput.mIteratorRwa = NodeDataIteratorRwa(input.at(mInputIndex).GetIterator(numSamples)); break; - case InputTypeBase::CONSTANT_ARRAY: + case InputIterationType::CONSTANT_ARRAY: mInput.mIterator = input.at(mInputIndex).GetArrayIterator(); break; } } // run on each node update (can be node specific) and will update node rwa filters - void NodeGraphInputAccessor::NodeUpdate(std::vector&, float updateRate) { + void InputAccessor::NodeUpdate(std::vector&, float updateRate) { switch (mType) { - case InputTypeBase::CUSTOM_INTERP_TWEEN: - case InputTypeBase::CUSTOM_INTERP_TWEEN_MS: + case InputIterationType::CUSTOM_INTERP_TWEEN: + case InputIterationType::CUSTOM_INTERP_TWEEN_MS: mInput.mTween.Update(updateRate); break; - case InputTypeBase::CUSTOM_INTERP_RWA_MS: // must set it manually - case InputTypeBase::SAMPLED: - case InputTypeBase::SAMPLED_RWA: - case InputTypeBase::CONSTANT_ARRAY: + case InputIterationType::CUSTOM_INTERP_RWA_MS: // must set it manually + case InputIterationType::SAMPLED: + case InputIterationType::SAMPLED_RWA: + case InputIterationType::CONSTANT_ARRAY: break; } } diff --git a/packages/nodegraph/source/common/operations/NodeGraphOpControl.cpp b/packages/nodegraph/source/common/operations/NodeGraphOpControl.cpp index f292b7d9..77aa4351 100644 --- a/packages/nodegraph/source/common/operations/NodeGraphOpControl.cpp +++ b/packages/nodegraph/source/common/operations/NodeGraphOpControl.cpp @@ -12,10 +12,10 @@ namespace l::nodegraph { /*********************************************************************/ void GraphControlBase::Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) { - mNodeInputManager.BatchUpdate(inputs, numSamples); + mInputManager.BatchUpdate(inputs, numSamples); - float sync = mNodeInputManager.GetValueNext(0); - mUpdateRate = mNodeInputManager.GetValueNext(1); + float sync = mInputManager.GetValueNext(0); + mUpdateRate = mInputManager.GetValueNext(1); if (sync > 0.5f) { mSamplesUntilUpdate = 0; } @@ -26,16 +26,16 @@ namespace l::nodegraph { mSamplesUntilUpdate = l::audio::BatchUpdate(mUpdateRate, mSamplesUntilUpdate, 0, numSamples, [&]() { - mNodeInputManager.NodeUpdate(inputs, mUpdateRate); - mUpdateRate = mNodeInputManager.GetValue(1); + mInputManager.NodeUpdate(inputs, mUpdateRate); + mUpdateRate = mInputManager.GetValue(1); - UpdateSignal(mNodeInputManager); + UpdateSignal(mInputManager); return mUpdateRate; }, [&](int32_t start, int32_t end, bool) { for (int32_t i = start; i < end; i++) { - auto [out0, out1] = ProcessSignal(mNodeInputManager); + auto [out0, out1] = ProcessSignal(mInputManager); *output0++ = out0; *output1++ = out1; } @@ -45,24 +45,24 @@ namespace l::nodegraph { /*********************************************************************/ - void GraphControlEnvelope::UpdateSignal(NodeInputManager&) { + void GraphControlEnvelope::UpdateSignal(InputManager&) { - mFreqTarget = mNodeInputManager.GetValueNext(6); + mFreqTarget = mInputManager.GetValueNext(6); if (mFreqTarget != 0.0f) { - mNodeInputManager.SetTarget(100, mFreqTarget); + mInputManager.SetTarget(100, mFreqTarget); } - mNodeInputManager.SetDuration(100, 1000.0f * mNodeInputManager.GetValueNext(3)); - mAttackFrames = l::audio::GetAudioTicksFromMS(mNodeInputManager.GetValueNext(4)); - mReleaseFrames = l::audio::GetAudioTicksFromMS(mNodeInputManager.GetValueNext(5)); - mAttackFactor = mNodeInputManager.GetValueNext(4); - mReleaseFactor = mNodeInputManager.GetValueNext(5); - - mNodeInputManager.SetUpdateRate(100, 1.0f); - mNodeInputManager.SetUpdateRate(101, 1.0f); + mInputManager.SetDuration(100, 1000.0f * mInputManager.GetValueNext(3)); + mAttackFrames = l::audio::GetAudioTicksFromMS(mInputManager.GetValueNext(4)); + mReleaseFrames = l::audio::GetAudioTicksFromMS(mInputManager.GetValueNext(5)); + mAttackFactor = mInputManager.GetValueNext(4); + mReleaseFactor = mInputManager.GetValueNext(5); + + mInputManager.SetUpdateRate(100, 1.0f); + mInputManager.SetUpdateRate(101, 1.0f); } - std::pair GraphControlEnvelope::ProcessSignal(NodeInputManager&) { - float velocity = mNodeInputManager.GetValueNext(2); + std::pair GraphControlEnvelope::ProcessSignal(InputManager&) { + float velocity = mInputManager.GetValueNext(2); float freq = 0.0f; bool noteOn = mFreqTarget != 0.0f; @@ -72,8 +72,8 @@ namespace l::nodegraph { mFreqTargetPrev = mFreqTarget; - mNodeInputManager.SetDuration(101, mAttackFactor); - mNodeInputManager.SetTarget(101, l::math::functions::sqrt(velocity)); + mInputManager.SetDuration(101, mAttackFactor); + mInputManager.SetTarget(101, l::math::functions::sqrt(velocity)); if (mFrameCount == 0) { // if note was off we set freq immediately @@ -84,8 +84,8 @@ namespace l::nodegraph { else if (!noteOn && mNoteOn) { mNoteOn = false; - mNodeInputManager.SetDuration(101, mReleaseFactor); - mNodeInputManager.SetTarget(101, 0.0f); + mInputManager.SetDuration(101, mReleaseFactor); + mInputManager.SetTarget(101, 0.0f); if (mFrameCount > 0 && mFrameCount < mAttackFrames + 1) { // still in attack so fast forward to release @@ -114,17 +114,17 @@ namespace l::nodegraph { } } - freq = mNodeInputManager.GetValueNext(100); + freq = mInputManager.GetValueNext(100); - float envelope = mNodeInputManager.GetValueNext(101); + float envelope = mInputManager.GetValueNext(101); return { freq, envelope }; } /*********************************************************************/ - void GraphControlArpeggio::UpdateSignal(NodeInputManager&) { - float attack = mNodeInputManager.GetValue(4); - float release = mNodeInputManager.GetValue(5); + void GraphControlArpeggio::UpdateSignal(InputManager&) { + float attack = mInputManager.GetValue(4); + float release = mInputManager.GetValue(5); float attackFactor = l::audio::GetRWAFactorFromMS(attack, 0.001f, gArpeggioUpdateRate); float releaseFactor = l::audio::GetRWAFactorFromMS(release, 0.001f, gArpeggioUpdateRate); mGainAttack = attackFactor; @@ -132,10 +132,10 @@ namespace l::nodegraph { if (mNotes.empty()) { mGainTarget = 0.0f; - mNodeInputManager.SetDuration(100, release, 0.01f); - mNodeInputManager.SetDuration(101, release, 0.01f); - mNodeInputManager.SetTarget(100, 0.0f); - mNodeInputManager.SetTarget(101, 1.0f); + mInputManager.SetDuration(100, release, 0.01f); + mInputManager.SetDuration(101, release, 0.01f); + mInputManager.SetTarget(100, 0.0f); + mInputManager.SetTarget(101, 1.0f); } else { if (mUpdateCounter % 2 == 0) { @@ -143,44 +143,44 @@ namespace l::nodegraph { mFreqTarget = l::audio::GetFrequencyFromNote(static_cast(mNotes.at(mNoteIndex))); mNoteIndex++; - auto velocity = mNodeInputManager.GetValueNext(2); - auto fade = mNodeInputManager.GetValueNext(3); + auto velocity = mInputManager.GetValueNext(2); + auto fade = mInputManager.GetValueNext(3); mGainTarget = 2.0f * velocity; mFreqSmoothing = fade; mFreqSmoothing *= mFreqSmoothing * 0.5f; - mNodeInputManager.SetDuration(100, attack, 0.01f); - mNodeInputManager.SetDuration(101, attack + release, 0.01f); - mNodeInputManager.SetTarget(100, 1.0f); - mNodeInputManager.SetTarget(101, 0.0f); + mInputManager.SetDuration(100, attack, 0.01f); + mInputManager.SetDuration(101, attack + release, 0.01f); + mInputManager.SetTarget(100, 1.0f); + mInputManager.SetTarget(101, 0.0f); //inputManager.SetValue(100, 0.0f); //inputManager.SetValue(101, 1.0f); mUpdateCounter = 0; } else { - mNodeInputManager.SetDuration(100, release, 0.01f); - mNodeInputManager.SetDuration(101, release, 0.01f); - mNodeInputManager.SetTarget(100, 0.0f); - mNodeInputManager.SetTarget(101, 1.0f); + mInputManager.SetDuration(100, release, 0.01f); + mInputManager.SetDuration(101, release, 0.01f); + mInputManager.SetTarget(100, 0.0f); + mInputManager.SetTarget(101, 1.0f); } mUpdateCounter++; } } void GraphControlArpeggio::Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) { - mNodeInputManager.BatchUpdate(inputs, numSamples); - float sync = mNodeInputManager.GetValueNext(0); - float bpm = mNodeInputManager.GetValueNext(1); + mInputManager.BatchUpdate(inputs, numSamples); + float sync = mInputManager.GetValueNext(0); + float bpm = mInputManager.GetValueNext(1); mUpdateRate = 44100.0f * 60.0f / (2.0f * 4.0f * bpm); if (sync > 0.5f) { mSamplesUntilUpdate = 0; } { - auto noteIdsOn = mNodeInputManager.GetArray(6); - auto noteIdsOff = mNodeInputManager.GetArray(7); + auto noteIdsOn = mInputManager.GetArray(6); + auto noteIdsOff = mInputManager.GetArray(7); if (!mNotes.empty()) { for (int32_t i = 0; i < gPolyphony; i++) { @@ -216,15 +216,15 @@ namespace l::nodegraph { auto output1 = outputs.at(1).GetIterator(numSamples, gArpeggioUpdateRate); mSamplesUntilUpdate = l::audio::BatchUpdate(mUpdateRate, mSamplesUntilUpdate, 0, numSamples, [&]() { - mNodeInputManager.NodeUpdate(inputs, mUpdateRate); - mUpdateRate = mNodeInputManager.GetValueNext(1); + mInputManager.NodeUpdate(inputs, mUpdateRate); + mUpdateRate = mInputManager.GetValueNext(1); - UpdateSignal(mNodeInputManager); + UpdateSignal(mInputManager); return mUpdateRate; }, [&](int32_t start, int32_t end, bool) { for (int32_t i = start; i < end; i++) { - float envelope = mNodeInputManager.GetValueNext(100); + float envelope = mInputManager.GetValueNext(100); float gain = mGainTarget * envelope; mGain += 0.0025f * (gain - mGain); diff --git a/packages/nodegraph/source/common/operations/NodeGraphOpFilter.cpp b/packages/nodegraph/source/common/operations/NodeGraphOpFilter.cpp index e2d00d73..f10b11bd 100644 --- a/packages/nodegraph/source/common/operations/NodeGraphOpFilter.cpp +++ b/packages/nodegraph/source/common/operations/NodeGraphOpFilter.cpp @@ -27,7 +27,7 @@ namespace l::nodegraph { } void GraphFilterBase::Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) { - mNodeInputManager.BatchUpdate(inputs, numSamples); + mInputManager.BatchUpdate(inputs, numSamples); mSync = inputs.at(0).Get(); if (mSync > 0.5f) { mNode->SetInput(1, 0.0f, numSamples); @@ -37,7 +37,7 @@ namespace l::nodegraph { mSamplesUntilUpdate = l::audio::BatchUpdate(mUpdateRate, mSamplesUntilUpdate, 0, numSamples, [&]() { - mNodeInputManager.NodeUpdate(inputs, mUpdateRate); + mInputManager.NodeUpdate(inputs, mUpdateRate); UpdateSignal(inputs, outputs); @@ -45,8 +45,8 @@ namespace l::nodegraph { }, [&](int32_t start, int32_t end, bool) { for (int32_t i = start; i < end; i++) { - float inputValue = mNodeInputManager.GetValueNext(1); - float signal = ProcessSignal(inputValue, mNodeInputManager.GetValueNext(2), mNodeInputManager.GetValueNext(3)); + float inputValue = mInputManager.GetValueNext(1); + float signal = ProcessSignal(inputValue, mInputManager.GetValueNext(2), mInputManager.GetValueNext(3)); *output++ = signal; } } @@ -104,8 +104,8 @@ namespace l::nodegraph { } void GraphFilterChamberlain2pole::UpdateSignal(std::vector&, std::vector&) { - mMode = static_cast(3.0f * mNodeInputManager.GetValueNext(mNumDefaultInputs + 0) + 0.5f); - mScale = l::math::functions::sqrt(mNodeInputManager.GetValueNext(3)); + mMode = static_cast(3.0f * mInputManager.GetValueNext(mNumDefaultInputs + 0) + 0.5f); + mScale = l::math::functions::sqrt(mInputManager.GetValueNext(3)); mScaleFilter.SetConvergenceFactor().SetTarget(mScale).SnapAt(); } diff --git a/packages/nodegraph/source/common/operations/NodeGraphOpOutput.cpp b/packages/nodegraph/source/common/operations/NodeGraphOpOutput.cpp index 152bbb23..1589a16a 100644 --- a/packages/nodegraph/source/common/operations/NodeGraphOpOutput.cpp +++ b/packages/nodegraph/source/common/operations/NodeGraphOpOutput.cpp @@ -87,13 +87,13 @@ namespace l::nodegraph { /*********************************************************************/ void GraphOutputPlot::Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) { - mNodeInputManager.BatchUpdate(inputs, numSamples); + mInputManager.BatchUpdate(inputs, numSamples); int32_t outputSize = outputs.at(0).GetSize(); float* output = &outputs.at(0).Get(outputSize); for (int32_t i = 0; i < numSamples; i++) { - output[mCurIndex] = mNodeInputManager.GetValueNext(0); + output[mCurIndex] = mInputManager.GetValueNext(0); mCurIndex = (mCurIndex + 1) % outputSize; } } diff --git a/packages/nodegraph/source/common/operations/NodeGraphOpSignal.cpp b/packages/nodegraph/source/common/operations/NodeGraphOpSignal.cpp index 6dcfbc47..0788a8cb 100644 --- a/packages/nodegraph/source/common/operations/NodeGraphOpSignal.cpp +++ b/packages/nodegraph/source/common/operations/NodeGraphOpSignal.cpp @@ -11,9 +11,9 @@ namespace l::nodegraph { /*********************************************************************/ void GraphSignalBase::Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) { - mNodeInputManager.BatchUpdate(inputs, numSamples); + mInputManager.BatchUpdate(inputs, numSamples); - float sync = mNodeInputManager.GetValueNext(0); + float sync = mInputManager.GetValueNext(0); if (sync > 0.5f) { mSamplesUntilUpdate = 0; } @@ -22,10 +22,10 @@ namespace l::nodegraph { mSamplesUntilUpdate = l::audio::BatchUpdate(mUpdateRate, mSamplesUntilUpdate, 0, numSamples, [&]() { - mUpdateRate = mNodeInputManager.GetValueNext(1); - mNodeInputManager.NodeUpdate(inputs, mUpdateRate); + mUpdateRate = mInputManager.GetValueNext(1); + mInputManager.NodeUpdate(inputs, mUpdateRate); - mSmooth = mNodeInputManager.GetValueNext(4); + mSmooth = mInputManager.GetValueNext(4); mRWAFreq.SetConvergenceInMs(1000.0f * (1.0f - mSmooth), 0.05f); @@ -37,10 +37,10 @@ namespace l::nodegraph { }, [&](int32_t start, int32_t end, bool) { for (int32_t i = start; i < end; i++) { - float freq = mNodeInputManager.GetValueNext(2); + float freq = mInputManager.GetValueNext(2); mRWAFreq.SetTarget(freq); float signalTarget = ProcessSignal(mDeltaTime, freq); - *output0++ = mNodeInputManager.GetValueNext(3) * signalTarget; + *output0++ = mInputManager.GetValueNext(3) * signalTarget; } } ); @@ -62,8 +62,8 @@ namespace l::nodegraph { } void GraphSignalSine2::UpdateSignal(std::vector&, std::vector&) { - mFmod = mNodeInputManager.GetValueNext(mNumDefaultInputs + 0); - mPmod = mNodeInputManager.GetValueNext(mNumDefaultInputs + 1); + mFmod = mInputManager.GetValueNext(mNumDefaultInputs + 0); + mPmod = mInputManager.GetValueNext(mNumDefaultInputs + 1); mFmod *= 0.25f * 0.25f * 0.5f * 44100.0f / l::math::functions::max(mFreq, 1.0f); mFilterFmod.SetConvergenceFactor().SetTarget(mFmod); @@ -103,12 +103,12 @@ namespace l::nodegraph { } void GraphSignalSaw2::UpdateSignal(std::vector&, std::vector&) { - mAttenuation = mNodeInputManager.GetValueNext(mNumDefaultInputs + 0); - mCutoff = mNodeInputManager.GetValueNext(mNumDefaultInputs + 1); - mNodeInputManager.SetUpdateRate(mNumDefaultInputs + 0, mUpdateRate); - mNodeInputManager.SetUpdateRate(mNumDefaultInputs + 1, mUpdateRate); - mNodeInputManager.SetDuration(mNumDefaultInputs + 0, 1.0f); - mNodeInputManager.SetDuration(mNumDefaultInputs + 1, 1.0f); + mAttenuation = mInputManager.GetValueNext(mNumDefaultInputs + 0); + mCutoff = mInputManager.GetValueNext(mNumDefaultInputs + 1); + mInputManager.SetUpdateRate(mNumDefaultInputs + 0, mUpdateRate); + mInputManager.SetUpdateRate(mNumDefaultInputs + 1, mUpdateRate); + mInputManager.SetDuration(mNumDefaultInputs + 0, 1.0f); + mInputManager.SetDuration(mNumDefaultInputs + 1, 1.0f); UpdateSaw(&mSaw, 0.00001f + 0.99999f * mAttenuation * mAttenuation * mAttenuation, mCutoff * mCutoff); } diff --git a/packages/nodegraph/tests/common/NodeGraphDataTest.cpp b/packages/nodegraph/tests/common/NodeGraphDataTest.cpp index 0a115213..199af215 100644 --- a/packages/nodegraph/tests/common/NodeGraphDataTest.cpp +++ b/packages/nodegraph/tests/common/NodeGraphDataTest.cpp @@ -15,21 +15,21 @@ class TestOp : public NodeGraphOp2 { TestOp(NodeGraphBase* node) : NodeGraphOp2(node, "TestOp") { - AddInput2(InputTypeBase::SAMPLED, "In 0", 0.0f, 1, 0.0f, 1.0f); - AddInput2(InputTypeBase::SAMPLED_RWA, "In 1", 0.0f, 1, 0.0f, 1.0f); + AddInput2(InputIterationType::SAMPLED, "In 0", 0.0f, 1, 0.0f, 1.0f); + AddInput2(InputIterationType::SAMPLED_RWA, "In 1", 0.0f, 1, 0.0f, 1.0f); AddOutput("Out 0"); AddOutput("Out 1"); } virtual ~TestOp() = default; virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override { - mNodeInputManager.BatchUpdate(inputs, numSamples); + mInputManager.BatchUpdate(inputs, numSamples); auto out0 = outputs.at(0).GetIterator(numSamples, 4.0f); auto out1 = outputs.at(1).GetIterator(numSamples, 4.0f); for (int i = 0; i < numSamples; i++) { - float value0 = mNodeInputManager.GetValueNext(0); - float value1 = mNodeInputManager.GetValueNext(1); + float value0 = mInputManager.GetValueNext(0); + float value1 = mInputManager.GetValueNext(1); *out0++ = value0; *out1++ = value1; @@ -71,24 +71,24 @@ class TestOp2 : public NodeGraphOp2 { TestOp2(NodeGraphBase* node) : NodeGraphOp2(node, "TestOp") { - AddInput2(InputTypeBase::CUSTOM_INTERP_TWEEN, "In 0", 0.0f, 1, 0.0f, 1.0f); - AddInput2(InputTypeBase::CUSTOM_INTERP_TWEEN_MS, "In 1", 0.0f, 1, 0.0f, 1.0f); + AddInput2(InputIterationType::CUSTOM_INTERP_TWEEN, "In 0", 0.0f, 1, 0.0f, 1.0f); + AddInput2(InputIterationType::CUSTOM_INTERP_TWEEN_MS, "In 1", 0.0f, 1, 0.0f, 1.0f); AddOutput("Out 0"); AddOutput("Out 1"); } virtual ~TestOp2() = default; virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override { - mNodeInputManager.BatchUpdate(inputs, numSamples); + mInputManager.BatchUpdate(inputs, numSamples); auto out0 = outputs.at(0).GetIterator(numSamples, 4.0f); auto out1 = outputs.at(1).GetIterator(numSamples, 4.0f); - mNodeInputManager.SetDuration(0, 24.0f, 0.1); - mNodeInputManager.SetDuration(1, 0.5f, 0.1); + mInputManager.SetDuration(0, 24.0f, 0.1); + mInputManager.SetDuration(1, 0.5f, 0.1); for (int i = 0; i < numSamples; i++) { - float value0 = mNodeInputManager.GetValueNext(0); - float value1 = mNodeInputManager.GetValueNext(1); + float value0 = mInputManager.GetValueNext(0); + float value1 = mInputManager.GetValueNext(1); *out0++ = value0; *out1++ = value1; From e28bb42cf59310eab4cf5ef0c770268527795f6b Mon Sep 17 00:00:00 2001 From: lnd3 Date: Wed, 25 Sep 2024 15:23:44 +0200 Subject: [PATCH 42/42] Docs. --- packages/nodegraph/include/nodegraph/core/NodeGraphData.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/nodegraph/include/nodegraph/core/NodeGraphData.h b/packages/nodegraph/include/nodegraph/core/NodeGraphData.h index f2c6c46b..a031aa18 100644 --- a/packages/nodegraph/include/nodegraph/core/NodeGraphData.h +++ b/packages/nodegraph/include/nodegraph/core/NodeGraphData.h @@ -33,7 +33,7 @@ namespace l::nodegraph { enum class OutputType { Default, // node will be processed if it is connected to the groups output by some route ExternalOutput, // node does not have meaningful output for other nodes but should still be processed (ex speaker output only has input) - ExternalVisualOutput, + ExternalVisualOutput, // node has visual output that requires special handling, for example the graph plot node }; std::pair GetInputBounds(InputBound bound);