HISE Logo Forum
    • Categories
    • Register
    • Login

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

    Scheduled Pinned Locked Moved Solved General Questions
    34 Posts 3 Posters 817 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

      @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
                        • griffinboyG
                          griffinboy @ustk
                          last edited by

                          @ustk

                          GCs are the only way to send an array into a node, unless you encode it as audio or other.

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

                            @griffinboy oh that's what I was thinking, @Christoph-Hart's shortcut needed a sequel 😆

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

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

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

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

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

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

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

                                  @ustk the table UI component is exactly that - a path with (quadratic) curves that are rendered to a float buffer and can be send to a C++ class over the ExternalData API. If for some reason the Table doesn't fit your requirements then yes your approach would be the next best thing.

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

                                    @Christoph-Hart I am making a cubic B-spline so a quadratic is somewhat underwhelming lol 😆

                                    But that would be so much easier

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

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

                                      @Christoph-Hart Do you think making the table to be working with any kind of custom spline (using a custom object) can be a serious request?
                                      Since there are many type of spline behaviour out there...

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

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

                                      45

                                      Online

                                      1.8k

                                      Users

                                      12.1k

                                      Topics

                                      105.2k

                                      Posts