HISE Logo Forum
    • Categories
    • Register
    • Login

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

    Scheduled Pinned Locked Moved Solved General Questions
    18 Posts 2 Posters 509 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
      last edited by griffinboy

      What's the best method for monitoring changes inside of an external c++ node? I want to be able to check the state of some variables inside my node, from the main Ui.

      Content.makeFrontInterface(300, 300);
      
      // Broadcaster definition
      const var Monitor = Engine.createBroadcaster({
        "id": "SliderPack Monitor",
        "colour": -1, // Optional, can be omitted or set to a specific color
        "args": ["processorId", "dataIndex", "value"]
      });
      
      // Attach to the correct data path and index for the SliderPack
      Monitor.attachToComplexData("SliderPack.Content", 
                                  "Script FX1", // Processor ID
                                  [0], // Indices of the sliders you want to monitor
                                  "Monitor changes in the first slider of Script FX1");
      
      // Attach listener to monitor changes in the SliderPack
      Monitor.addListener(0, "MonitorSliderPack", function(processorId, dataIndex, value){
          Console.print("Change detected in SliderPack:");
          Console.print("Processor ID: " + processorId);
          Console.print("Data Index: " + dataIndex);
          Console.print("Encoded Value: " + value);
      
              //var decodedValue = Engine.decodeBase64ValueTree( doubleToString(value,0) );
              //Console.print("Decoded Value: " + decodedValue);
      
      
          // ADD CODE HERE to handle the change...
      });
      

      One of my ideas was to use something like a sliderpack or table, which would allow me to communicate fairly easily (although the code above isn't quite right, for some reason I'm getting a base64 for the whole pack rather than the intended first slider)

      Not to mention this cannot be the best way in the first place.

      Christoph HartC 1 Reply Last reply Reply Quote 0
      • griffinboyG griffinboy marked this topic as a question on
      • Christoph HartC
        Christoph Hart @griffinboy
        last edited by

        @griffinboy actually this is currently the best method as the communication channel from the C++ node back to HISE is very VERY limited at the moment. Basically you have two options:

        • use a slider pack / table with its change event messages.
        • use a modulation output and drag that around.

        But there might be the option of using a global cable as it should theoretically work with compiled scriptnode networks so you should be able to do the same with a C++ node. I'll look into it now as I'm sitting on something in this area anyways currently.

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

          @Christoph-Hart

          Thanks for the quick response.
          What am I doing wrong in my attempt? I am getting a base64 type sting representing all sliders when I just want to query the first

          Hise

          Content.makeFrontInterface(300, 300);
          
          // Broadcaster definition
          const var Monitor = Engine.createBroadcaster({
            "id": "SliderPack Monitor",
            "colour": -1, // Optional, can be omitted or set to a specific color
            "args": ["processorId", "dataIndex", "value"]
          });
          
          // Attach to the correct data path and index for the SliderPack
          Monitor.attachToComplexData("SliderPack.Content", 
                                      "Script FX1", // Processor ID
                                      [0], // Indices of the sliders you want to monitor
                                      "Monitor changes in the first slider of Script FX1");
          
          // Attach listener to monitor changes in the SliderPack
          Monitor.addListener(0, "MonitorSliderPack", function(processorId, dataIndex, value){
              Console.print("Change detected in SliderPack:");
              Console.print("Processor ID: " + processorId);
              Console.print("Data Index: " + dataIndex);
              Console.print("Encoded Value: " + value);
          
                  //var decodedValue = Engine.decodeBase64ValueTree( doubleToString(value,0) );
                  //Console.print("Decoded Value: " + decodedValue);
          
          
              // ADD CODE HERE to handle the change...
          });
          
          

          C++

            template <int NV> struct Out_Lufs : public data::base
            {
                // Metadata Definitions ------------------------------------------------------------------------
          
                SNEX_NODE(Out_Lufs);
          
                struct MetadataClass
                {
                    SN_NODE_ID("Out_Lufs");
                };
          
                static constexpr bool isModNode() { return true; }
                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; }
          
                static constexpr int NumTables = 0; // No tables
                static constexpr int NumSliderPacks = 1; // Define one SliderPack
                static constexpr int NumAudioFiles = 0;
                static constexpr int NumFilters = 0;
                static constexpr int NumDisplayBuffers = 0;
          
                float* sliderPackData = nullptr; // Pointer to the SliderPack data
          
                // Method to set external data (SliderPack values)
                void setExternalData(const ExternalData& d, int index)
                {
                    if (index == 0) // Assuming we have only one SliderPack
                    {
                        sliderPackData = reinterpret_cast<float*>(d.data);
                        initializeSliderPackData(); // Initialize SliderPack data after setting the pointer
                    }
                }
          
                // Method to initialize the SliderPack data
                void initializeSliderPackData()
                {
                    if (sliderPackData != nullptr)
                    {
                        sliderPackData[0] = 0.5f; // Set the first slider to an initial value
                    }
                }
          
                // Public method to modify the SliderPack data
                void modifySliderPackData(float value)
                {
                    if (sliderPackData != nullptr)
                    {
                        sliderPackData[0] = value; // Set the first slider to the specified value
                    }
                }
          
          Christoph HartC 1 Reply Last reply Reply Quote 0
          • Christoph HartC
            Christoph Hart @griffinboy
            last edited by

            @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

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

              @Christoph-Hart

              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?

              756d0f59-2db5-47ec-b990-d2ba18b55fb0-image.png ```

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

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

                1. Create all your global cables
                2. Call the function in the HISE menu (Tools -> Create C++ code for global cables)
                3. Copy the code snippet into your node and subclass your node class from the type definition
                4. Send a double value anywhere from your node class using setGlobalCableValue<>(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 call setGlobalCableValue<GlobalCables::cable_funky>(0.2) to send the value that can be picked up anywhere in HISE.

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

                  This post is deleted!
                  1 Reply Last reply Reply Quote 0
                  • 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.

                                        1 Reply Last reply Reply Quote 0
                                        • griffinboyG griffinboy has marked this topic as solved on
                                        • First post
                                          Last post

                                        44

                                        Online

                                        1.7k

                                        Users

                                        11.7k

                                        Topics

                                        102.3k

                                        Posts