HISE Logo Forum
    • Categories
    • Register
    • Login

    Third party node modulation output slot

    Scheduled Pinned Locked Moved Solved C++ Development
    13 Posts 4 Posters 145 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.
    • HISEnbergH
      HISEnberg @ustk
      last edited by

      @ustk I don't think this is exactly what you are asking for, but you can access the same display/modulation system as can be found in the scriptnode-dynamics (Gate, Compressor, Peak, etc.).

      ScreenRecording2025-10-21at1.34.53PM-ezgif.com-video-to-gif-converter.gif

      It's a bit less time consuming than setting up the global cable-> C++ system.

      All you really need to do:

      1. If you want the display, inherit from:
      public data::display_buffer_base<true>
      
      1. Meta-definitions:
          static constexpr bool isModNode() { return true; };
          static constexpr bool isNormalisedModulation() { return true; }; // optional
      
      1. Prepare the display buffer:
      void prepare(PrepareSpecs specs) override
      {
          display_buffer_base<true>::prepare(specs);  
      }
      
      1. Return the mod value in handleModulaiton:
      bool handleModulation(double& v)
      {
          v = 1.0 - (double)getYourFunction();  
          
          // Update display buffer 
          updateBuffer(v, lastNumSamples);
          
          return true;  // Send Modulaiton!
      }
      
      ustkU 1 Reply Last reply Reply Quote 1
      • ustkU
        ustk @HISEnberg
        last edited by ustk

        @HISEnberg Oh mate I think this is exactly what I need! I didn't see the isModNode existence, shame on me...

        So this is necessarily paired with the display buffer?
        Not a huge deal but it'd less ugly if I only have the cable connector since the value I'm sending is a latency compensation of my DSP to surrounding nodes...

        Hise made me an F5 dude, browser just suffers...

        Christoph HartC HISEnbergH 3 Replies Last reply Reply Quote 0
        • Christoph HartC
          Christoph Hart @ustk
          last edited by

          @ustk sure you can just use the mod output. Just make sure to

          // if you define this, then the mod dragger will appear in your node UI.
          static constexpr bool isModNode() { return true; };
          
          // the latency is probably a non-normalised value
          static constexpr bool isNormalisedModulation() { return false; };
          
          // Let's just send it once 
          bool initialised = false;
          
          bool handleModulation(double& v)
          {
              if(!initialised)
              {
                  v = calculateYourLatency();
                  initialised = true;
                  return true;
              else
                  return false;
          }
          
          ustkU 1 Reply Last reply Reply Quote 2
          • ustkU
            ustk @Christoph Hart
            last edited by

            @Christoph-Hart Yeah I ended up trying just that end it worked at first try! Amaaaazing guys 😎

            Hise made me an F5 dude, browser just suffers...

            1 Reply Last reply Reply Quote 0
            • HISEnbergH
              HISEnberg @ustk
              last edited by

              @ustk No not all you can build it without the display buffer, that is just an option I included. Here is the most bare bones example I can think of. This should work in Scriptnode:

              // ==================================| Third Party Node Template |==================================
              
              #pragma once
              #include <JuceHeader.h>
              
              namespace project
              {
              using namespace juce;
              using namespace hise;
              using namespace scriptnode;
              
              // ==========================| The node class with all required callbacks |==========================
              
              template <int NV> struct mod_example: public data::base
              {
              	// Metadata Definitions ------------------------------------------------------------------------
              	
              	SNEX_NODE(mod_example);
              	
              	struct MetadataClass
              	{
              		SN_NODE_ID("mod_example");
              	};
              	
              	// set to true if you want this node to have a modulation dragger
              	static constexpr bool isModNode() { return true; }; // SET TO TRUE!
              	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 ------------------------------------------------------------------------
              	
                  float modValue = 0.0f; // STORE MOD VALUE
              
              	void prepare(PrepareSpecs specs)
              	{
              		
              	}
              	
              	void reset()
              	{
                      modValue = 0.0f; // RESET MOD VALUE
              	}
              	
              	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)
              	{
                      // EXAMPLE, JUST CLACULATE THE PEAK-STEREO SIGNAL
                      float left = std::abs(data[0]);
                      float right = std::abs(data[1]);
                      float peak = std::max(left, right);
                      
                      // STORE FOR MOD OUTPUT
                      modValue = std::min(peak, 1.0f);
              	}
              	
              	int handleModulation(double& value)
              	{
                      value = (double)modValue; // OUTPUT MOD VALUE
              
              		return 1; //SET THIS TO 1 TO SEND THE VALUE OUT TO HISE!!!!!
              		
              	}
              	
              	void setExternalData(const ExternalData& data, int index)
              	{
              		
              	}
              	// Parameter Functions -------------------------------------------------------------------------
              	
              	template <int P> void setParameter(double v)
              	{
              		
              	}
              	
              	void createParameters(ParameterDataList& data)
              	{
              	
              	}
              };
              }
              
              ustkU 1 Reply Last reply Reply Quote 2
              • ustkU
                ustk @HISEnberg
                last edited by

                @HISEnberg Yeah perfect that is what I did, just the mod output...
                But you did well teaching me with the display buffer as well because I will soon need it! 👨🏫
                Thanks a lot!

                Hise made me an F5 dude, browser just suffers...

                1 Reply Last reply Reply Quote 1
                • ustkU ustk marked this topic as a question
                • ustkU ustk has marked this topic as solved
                • HISEnbergH
                  HISEnberg @ustk
                  last edited by

                  @ustk Sending the latency out is a pretty interesting idea. I'm currently trying to figure out how to do this in one of my plugins that needs have 0 latency between the dry and wet. Is the idea to offset the dry signal by an equal amount of latency as is introduced by the wet signal?

                  I am really trying to dig into this topic and the best solutions for it. One of the biggest problems I am experiencing lately is fractional sample latency (I think the grown ups call this phase offset 😆 ).

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

                    @HISEnberg Yeah this is exactly what I am doing, sending my node's latency to the dry signal through a SampleToMs converter connected to a delay.
                    My DSP throws out the latency in round samples so I don't know about the fractional delay since I just directly convert it to ms.

                    In your case, you might be able to deal with the fractional delay if you directly send it in ms to a Juce delay like lagrange or thiran. They are meant to deal with fractional delay unless I'm wrong...

                    Hise made me an F5 dude, browser just suffers...

                    HISEnbergH 1 Reply Last reply Reply Quote 1
                    • HISEnbergH
                      HISEnberg @ustk
                      last edited by

                      @ustk nice I basically setup the same system but just using HISE’s version of get/setLatencyInSamples. I’m assuming you are using the JUCE version of this in the C++ node? I believe HISEs API is exactly the same but I could be wrong.
                      https://docs.juce.com/master/classjuce_1_1AudioProcessor.html#a21b7f750657b54b09066bcbf3264506d

                      Unfortunately I can’t find any information about how to get a fractional latency sample, which kind of makes sense all things considered. There’s not many forum posts about this topic either, much to my surprise. Maybe @griffinboy has some ideas since I know he often sketches out his algorithms in MATLAB or Python, they maybe offer more robust ways of addressing this. I’ve just been eyeballing my results in my DAW and using Plugin Doctor but there must be better ways to dealing with this!

                      Christoph HartC griffinboyG ustkU 3 Replies Last reply Reply Quote 0
                      • Christoph HartC
                        Christoph Hart @HISEnberg
                        last edited by

                        I'm pretty sure that DAWs will not respect your fractional sample value - they just use it to offset the buffers they send you and I don't think any DAW will interpolate this.

                        The fact that the JUCE method's signature is

                        void juce::AudioProcessor::setLatencySamples(int newLatency)	
                                                                     ^^^
                        

                        should be a good hint about the industry practice here.

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

                          @HISEnberg

                          Yeah if you're talking about phase alignment I do it all the time.

                          Latency compensation is always in whole samples though, but there's no reason you can't phase align things fractionally by introducing more latency to either reach a whole number and then report that whole number to the daw, or in the cases where you're in control of both things that you want to align, just fractionally delay one of those things until it matches the other.

                          A daw won't respect fractional latency though if you're trying to get the daw to compensate. That's why lots of plugins have internal mixers so that they can phase align dry and wet for example (by delaying the dry path so that it will be in phase with the wet).

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

                            @HISEnberg said in Third party node modulation output slot:

                            @ustk nice I basically setup the same system but just using HISE’s version of get/setLatencyInSamples. I’m assuming you are using the JUCE version of this in the C++ node? I believe HISEs API is exactly the same but I could be wrong.

                            Well my use case is different, I just compute my inner DSP latency for dry/mix situation in a split node, not reporting the whole latency of my project to the DAW...

                            Regarding what @Christoph-Hart and @griffinboy are saying, could oversampling help here?
                            Despite the fact it'll eat up some more CPU, of course...

                            Hise made me an F5 dude, browser just suffers...

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

                            14

                            Online

                            2.0k

                            Users

                            12.7k

                            Topics

                            110.5k

                            Posts