Monitor changes inside Scriptfx - the correct method? (broadcasters listeners)
-
@griffinboy if you listen to the Content event type then it will give you the full data. Try the DisplayIndex event, then you get the changed index
-
Sorry I'm still struggling
I'm trying to get back the value of the first slider.Is it normal for it to return a base64?
```
-
@griffinboy let's ditch that approach, it's hacky anyways.
I've committed a fix for the global cable not working in a compiled plugin and added a helper interface class that lets you register and send values through global cables super conveniently. Now the workflow is as simple as:
- Create all your global cables
- Call the function in the HISE menu (Tools -> Create C++ code for global cables)
- Copy the code snippet into your node and subclass your node class from the type definition
- Send a
double
value anywhere from your node class usingsetGlobalCableValue<>(double v)
This is the generated code for my test project:
// Use this enum to refer to the cables, eg. this->setGlobalCableValue<GlobalCables::cable_funky>(0.4) enum class GlobalCables { cable_funky = 0, second_cable = 1 }; // Subclass your node from this using cable_manager_t = routing::global_cable_cpp_manager<SN_GLOBAL_CABLE(1077699755), SN_GLOBAL_CABLE(-376951630)>;
So in this case you just have to subclass your node from
cable_manager_t
, then callsetGlobalCableValue<GlobalCables::cable_funky>(0.2)
to send the value that can be picked up anywhere in HISE. -
This post is deleted! -
This post is deleted! -
What am I misunderstanding?
I don't think I am doing it right lol.
(My c++ knowdege has a lot of holes, I need to take a deep dive and really get to know some of these features)// ==================================| Third Party Node Template |================================== #pragma once #include <JuceHeader.h> namespace project { using namespace juce; using namespace hise; using namespace scriptnode; // Use this enum to refer to the cables enum class GlobalCables { GC1 = 0 }; // Subclass your node from this using cable_manager_t = routing::global_cable_cpp_manager<SN_GLOBAL_CABLE(70357)>; // ==========================| The node class with all required callbacks |========================== template <int NV> struct testnode: public data::base, public cable_manager_t { // Metadata Definitions ------------------------------------------------------------------------ SNEX_NODE(testnode); struct MetadataClass { SN_NODE_ID("testnode"); }; // set to true if you want this node to have a modulation dragger static constexpr bool isModNode() { return false; }; static constexpr bool isPolyphonic() { return NV > 1; }; // set to true if your node produces a tail static constexpr bool hasTail() { return false; }; // set to true if your doesn't generate sound from silence and can be suspended when the input signal is silent static constexpr bool isSuspendedOnSilence() { return false; }; // Undefine this method if you want a dynamic channel count static constexpr int getFixChannelAmount() { return 2; }; // Define the amount and types of external data slots you want to use 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; // Scriptnode Callbacks ------------------------------------------------------------------------ void prepare(PrepareSpecs specs) { } void reset() { } void handleHiseEvent(HiseEvent& e) { } template <typename T> void process(T& data) { static constexpr int NumChannels = getFixChannelAmount(); // Cast the dynamic channel data to a fixed channel amount auto& fixData = data.template as<ProcessData<NumChannels>>(); // Create a FrameProcessor object auto fd = fixData.toFrameData(); while(fd.next()) { // Forward to frame processing processFrame(fd.toSpan()); } } template <typename T> void processFrame(T& data) { } int handleModulation(double& value) { return 0; } void setExternalData(const ExternalData& data, int index) { } // Parameter Functions ------------------------------------------------------------------------- template <int P> void setParameter(double v) { if (P == 0) { // Use the parameter to control GC1 this->setGlobalCableValue<GlobalCables::GC1>(v); } } void createParameters(ParameterDataList& data) { { // Rename the parameter to "GC1 Control 2" parameter::data p("GC1 Control", { 0.0, 1.0 }); registerCallback<0>(p); p.setDefaultValue(0.5); data.add(std::move(p)); } } }; }
-
@griffinboy Actually that looks exactly what I have been doing in my test project. What's the problem here? Does it not compile or does it not work?
Remember you have to initialize and create the global cable with the exact ID you used when creating this code snippet BEFORE you load the effect (or use Recompile all scripts to reinitialize the connections afterwards).
-
The global cable doesn't update when you move the knob.
Maybe my Ui code is at fault?
Content.makeFrontInterface(300, 300); const var rm = Engine.getGlobalRoutingManager(); // Create the global cable const var mc = rm.getCable("GC1"); // Add another panel. This will just display a circle with the alpha value modulated const var P2 = Content.addPanel("P2", 110, 50); // Now instead of the timer we use the asynchronous UI callback provided by the cable. mc.registerCallback(function(value) { // Write the value into the data object and repaint the panel P2.data.val = value; P2.repaint(); }, AsyncNotification); P2.setPaintRoutine(function(g) { g.fillAll(0x22FFFFFF); g.setColour(Colours.withAlpha(Colours.white, this.data.val)); // Rawdogging the area like a pro... g.fillEllipse([25, 0, 50, 50]); });
Yeah, recompile all scripts didn't work, and also there is a glitch that has developed where when you click to add a new scriptnode to the tree, you cannot close the dialogue box.
-
This post is deleted! -
Bump
-
Anything I can do to help debug it?
*edit, I saw that a while ago you uploaded a new build, I'll install it in the morning and see if maybe I just caught an in-between build.
-
@griffinboy I looked into yesterday because it worked for me but then I realized that we're talking about different things. I was checking the Hardcoded FX and you are probably loading the node in a network (because there it doesn't work yet).
I tried to fix it but there is one nasty persistent crash when unloading the nodes that connect to a cable that I still have to fix. In the meantime you can check the procedure with the hardcoded FX module, there it should work.
-
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...