HISE Logo Forum
    • Categories
    • Register
    • Login

    Monitor changes inside Scriptfx - the correct method? (broadcasters listeners)

    Scheduled Pinned Locked Moved Solved General Questions
    32 Posts 3 Posters 782 Views
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • griffinboyG
      griffinboy @Christoph Hart
      last edited by griffinboy

      This post is deleted!
      1 Reply Last reply Reply Quote 0
      • griffinboyG
        griffinboy @Christoph Hart
        last edited by griffinboy

        @Christoph-Hart

        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));
                }
            }
        };
        }
        
        Christoph HartC 1 Reply Last reply Reply Quote 0
        • Christoph HartC
          Christoph Hart @griffinboy
          last edited by

          @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).

          griffinboyG 3 Replies Last reply Reply Quote 0
          • griffinboyG
            griffinboy @Christoph Hart
            last edited by griffinboy

            @Christoph-Hart

            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.

            c9381eb4-c796-46ef-8908-b43d05e733f7-image.png

            griffinboyG 1 Reply Last reply Reply Quote 0
            • griffinboyG
              griffinboy @Christoph Hart
              last edited by

              This post is deleted!
              1 Reply Last reply Reply Quote 0
              • griffinboyG
                griffinboy @griffinboy
                last edited by

                @griffinboy

                Bump

                1 Reply Last reply Reply Quote 0
                • griffinboyG
                  griffinboy @Christoph Hart
                  last edited by griffinboy

                  @Christoph-Hart

                  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.

                  Christoph HartC 1 Reply Last reply Reply Quote 0
                  • Christoph HartC
                    Christoph Hart @griffinboy
                    last edited by

                    @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.

                    Christoph HartC griffinboyG 2 Replies Last reply Reply Quote 0
                    • Christoph HartC
                      Christoph Hart @Christoph Hart
                      last edited by

                      Alright, please check it again, now it should work.

                      griffinboyG 1 Reply Last reply Reply Quote 2
                      • griffinboyG
                        griffinboy @Christoph Hart
                        last edited by

                        @Christoph-Hart

                        I'll check it! Thanks for the update.

                        1 Reply Last reply Reply Quote 0
                        • griffinboyG
                          griffinboy @Christoph Hart
                          last edited by griffinboy

                          @Christoph-Hart

                          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.

                          ustkU 1 Reply Last reply Reply Quote 0
                          • griffinboyG griffinboy has marked this topic as solved on
                          • ustkU
                            ustk @griffinboy
                            last edited by

                            @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?

                            Can't help pressing F5 in the forum...

                            griffinboyG 1 Reply Last reply Reply Quote 0
                            • griffinboyG
                              griffinboy @ustk
                              last edited by

                              @ustk

                              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;
                                  };
                              }
                              
                              
                              ustkU 1 Reply Last reply Reply Quote 1
                              • ustkU
                                ustk @griffinboy
                                last edited by

                                @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

                                Can't help pressing F5 in the forum...

                                griffinboyG 1 Reply Last reply Reply Quote 0
                                • griffinboyG
                                  griffinboy @ustk
                                  last edited by griffinboy

                                  @ustk

                                  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

                                  ustkU 2 Replies Last reply Reply Quote 1
                                  • ustkU
                                    ustk @griffinboy
                                    last edited by

                                    @griffinboy I tried many keywords already with no luck...

                                    Can't help pressing F5 in the forum...

                                    Christoph HartC 1 Reply Last reply Reply Quote 0
                                    • ustkU
                                      ustk @griffinboy
                                      last edited by ustk

                                      @griffinboy Cool I got it to compile with the callback! ☺
                                      Now it's a matter of properly reading the JSON I am sending... -> Working

                                      UI 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 update gain

                                      EDIT: my bad... wasn't listening on the right set of speakers lol 🤣 WORKING LKE A CHARM!!!
                                      Thanks a lot @griffinboy !

                                      Can't help pressing F5 in the forum...

                                      griffinboyG 1 Reply Last reply Reply Quote 2
                                      • griffinboyG
                                        griffinboy @ustk
                                        last edited by

                                        @ustk

                                        Nice thanks for posting the working answer

                                        1 Reply Last reply Reply Quote 0
                                        • Christoph HartC
                                          Christoph Hart @ustk
                                          last edited by Christoph Hart

                                          @ustk said in Monitor changes inside Scriptfx - the correct method? (broadcasters listeners):

                                          @griffinboy I tried many keywords already with no luck...

                                          In the docs:

                                          Link Preview Image
                                          HISE | ScriptNode | global_cable

                                          Send a double precision float value anywhere to HISE

                                          favicon

                                          (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).

                                          ustkU 1 Reply Last reply Reply Quote 2
                                          • ustkU
                                            ustk @Christoph Hart
                                            last edited by

                                            @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

                                            Can't help pressing F5 in the forum...

                                            griffinboyG 1 Reply Last reply Reply Quote 0
                                            • First post
                                              Last post

                                            28

                                            Online

                                            1.8k

                                            Users

                                            12.1k

                                            Topics

                                            105.1k

                                            Posts