Monitor changes inside Scriptfx - the correct method? (broadcasters listeners)
-
Alright, please check it again, now it should work.
-
I'll check it! Thanks for the update.
-
It works! Thank you!
I'll do a video on this soon in my scriptnode c++ series. I've been remaking the first few videos to make more sense and go into better depth. -
G griffinboy has marked this topic as solved on
-
@griffinboy @Christoph-Hart What is the way to send the value to the node from the UI?
Is there a callback that should be defined? -
Global cables can be used.
// Griffin_Node.h // ----------------------------------------------------------------------------- #pragma once #include <JuceHeader.h> //#include "src/.h" //#include "src/.h" //#include "src/.h" //#include "src/.h" //#include "src/Utils_DelayInterpolation.h" //#include "src/Utils_General.h" //#include "src/Utils_XSimd.h" namespace project { #ifndef M_PI #define M_PI 3.14159265358979323846 #endif using namespace juce; using namespace hise; using namespace scriptnode; /* Griffin_Node ----------- Description of entire program + specs */ /* // Global Cable Example (communicate arbitrary data to Hise) enum class GlobalCables { dataCable = 0 }; using cable_manager_t = routing::global_cable_cpp_manager<SN_GLOBAL_CABLE(-389806413)>; // Template the node with this template <int NV> struct Griffin_Node: public data::base, public cable_manager_t { // In c++ we can then: // create a json object hise::JSONObject obj; // write values into the JSON object obj["thingies"] = [0,1,2,3,4]; // send the object back to HISE this->sendDataToGlobalCable<GlobalCables::dataCable>(obj); // thus we can send any type of data to Hise // then in Hise we can do: const var rm = Engine.getGlobalRoutingManager(); const var cableName = rm.getCable("cableName"); cableName.registerDataCallback(function(data) { Console.print("DATA: " + trace(data)); }); */ // pre C++20 // You cannot change the way this node is templated template <int NV> // name struct Griffin_Node : public data::base { // name SNEX_NODE(Griffin_Node); struct MetadataClass { // name SN_NODE_ID("Griffin_Node"); }; // name Griffin_Node() { // Callback for global cables // (from HISE to your C++ node, this is the way to register callbacks). //this->registerDataCallback<GlobalCables::dataCable>([](const var& funky) //{ // jassertfalse; //}); } //============================================================================== // Node Properties //============================================================================== static constexpr bool isModNode() { return false; } static constexpr bool isPolyphonic() { return NV > 1; } static constexpr bool hasTail() { return false; } static constexpr bool isSuspendedOnSilence() { return false; } static constexpr int getFixChannelAmount() { return 2; } // Add external data slots to the node UI static constexpr int NumTables = 0; static constexpr int NumSliderPacks = 0; static constexpr int NumAudioFiles = 0; static constexpr int NumFilters = 0; static constexpr int NumDisplayBuffers = 0; /* AudioEffect ----------- Description */ class AudioEffect { public: AudioEffect(float initiaParam = 5000.0f) { localParam = initiaParam; } // Initialize the effect if needed void prepare(double sampleRate) { } // Process a block of samples inline void process(float* samples, int numSamples) { // Iterate over the samples for (int i = 0; i < numSamples; ++i) { } } // Update the parameters inline void updateParam1(float newParam) { localParam = newParam; } private: float localParam; }; /* Main Node ----------- Description */ // Initialize the effect void prepare(PrepareSpecs specs) { float sampleRate = specs.sampleRate; float numChannels = specs.numChannels; // Initialize both effects (does nothing by default) leftChannelEffect.prepare(sampleRate); rightChannelEffect.prepare(sampleRate); } // Reset (called when the plugin is reloaded) void reset() {} // Process audio blocks (audio enters the node here) template <typename PD> inline void process(PD& d) { auto& fix = d.template as<ProcessData<2>>(); auto blk = fix.toAudioBlock(); float* L = blk.getChannelPointer(0); float* R = blk.getChannelPointer(1); int n = d.getNumSamples(); // Process each channel separately through their own audioEffect instance leftChannelEffect.process(L, n); rightChannelEffect.process(R, n); } /* Parameter Handling ----------- Description */ // Update parameters template <int P> inline void setParameter(double v) { if (P == 0) { leftChannelEffect.updateParam1(static_cast<float>(v)); rightChannelEffect.updateParam1(static_cast<float>(v)); } if (P == 1) { // Do something with value (v) } } // Create GUI parameters void createParameters(ParameterDataList& data) { { parameter::data p("Param", { 0.0, 1.0, 0.01 }); registerCallback<0>(p); p.setDefaultValue(5000.0); data.add(std::move(p)); } { parameter::data p("Param2", { 0.0, 1.0, 0.01 }); registerCallback<0>(p); p.setDefaultValue(5000.0); data.add(std::move(p)); } } // When this header file gets run for the first time, setParameter gets called with the default knob values /* External Data ----------- Description */ // span<dyn<float>, NUM_CHANNELS> externalBuffer; void setExternalData(const ExternalData& ed, int index) { /* if(data.isNotEmpty()) { // ExternalData data = ed; // float audioSampleRate = data.sampleRate; // float audioNumChannels = data.numChannels; // float audioNumSamples = data.numSamples; // ed.referBlockTo(externalBuffer[0], 0); // ed.referBlockTo(externalBuffer[1], 1); // Alternatively convert the data to the juce::AudioSampleBuffer class // auto buffer = data.toAudioSampleBuffer(); } */ } /* Hise Event ----------- Description */ void handleHiseEvent(HiseEvent& e) { /* if (e.isNoteOn()) { float note = e.getNoteNumber(); } */ } /* Modulation Output ----------- To enable, set isModNode() to true) */ // ModValue modOut; // bool handleModulation(double& value) // { // return modOut.getChangedValue(value); // } // Use this to call handleModulation // modOut.setModValueIfChanged( set mod to something ); //============================================================================== // Unused Functions (Required by compiler) //============================================================================== SN_EMPTY_PROCESS_FRAME; //SN_EMPTY_HANDLE_EVENT; //SN_EMPTY_SET_EXTERNAL_DATA; //SN_EMPTY_CREATE_PARAM; private: AudioEffect leftChannelEffect; AudioEffect rightChannelEffect; }; }
-
@griffinboy Yeah cable is what I am trying to use, I need to receive JSON data from UI.
Thanks for your template code it's awesome! Well... when I'll be able to make it work lol
-
Christoph wrote an example for exactly that somewhere... I wonder where it was I'm not sure, search for global cable posts!
That template above has a version of it. Use GPT to untangle my comments itll understand mostly what I was writing there
-
@griffinboy I tried many keywords already with no luck...
-
@griffinboy Cool I got it to compile with the callback!
Now it's a matter of properly reading the JSON I am sending...-> WorkingUI code:
const var RoutingManager = Engine.getGlobalRoutingManager(); const var DataCable = RoutingManager.getCable("dataCable"); const var data = {level: 0.0}; inline function onKnob1Control(component, value) { data.level = value; DataCable.sendData(data); };
C++
global_cable_data() { // Register a data callback for the global cable this->registerDataCallback<GlobalCables::dataCable>([this](const var& data) { if (auto* obj = data.getDynamicObject()) { const var& levelVar = obj->getProperty("level"); //if (levelVar.isDouble() || levelVar.isInt()) //{ gain = static_cast<float>(static_cast<double>(levelVar)); //} } }); }
This doesn't seem to updategain
EDIT: my bad... wasn't listening on the right set of speakers lol
WORKING LKE A CHARM!!!
Thanks a lot @griffinboy ! -
Nice thanks for posting the working answer
-
@ustk said in Monitor changes inside Scriptfx - the correct method? (broadcasters listeners):
@griffinboy I tried many keywords already with no luck...
In the docs:
HISE | ScriptNode | global_cable
Send a double precision float value anywhere to HISE
(docs.hise.dev)
What is the way to send the value to the node from the UI?
Is there a callback that should be defined?For UI -> Node communication, just use the stock parameter system. Only for Node -> UI communication (eg. reporting back analysis values from your C++ class) you need to use the global cables - and then you can choose between a realtimesafe single value or any arbitrary data that will be allocated and copied around over the DLL boundaries (which is not realtime safe).
-
@Christoph-Hart hmmm... have to look at this ! I made it to work UI -> global cable -> node, but I'll try with standard parameter to send an array of JSON
-
GCs are the only way to send an array into a node, unless you encode it as audio or other.
-
@griffinboy oh that's what I was thinking, @Christoph-Hart's shortcut needed a sequel
-
@ustk ah ok, I was just underwhelmed by the data you send over the cable in your example (a single gain number lol) but I have no idea what you're doing in the real world.
If you need to send more to your node, then yeah the global cable might be the best option.
-
@Christoph-Hart said in Monitor changes inside Scriptfx - the correct method? (broadcasters listeners):
@ustk ah ok, I was just underwhelmed by the data you send over the cable in your example (a single gain number lol)
Yeah you make me feel padawan like, which I probably am lol.
but I have no idea what you're doing in the real world.
I'm wondering the same thing every single day, until I'm uploaded to the cloud...
More seriously, I draw a spline in the interface, and send the control points to the node in order to reconstruct the wave shaper in a buffer...
Sounds the right approach or did I spend a week luring myself?