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.
    • ustkU
      ustk
      last edited by

      Yeah so I'm pretty sure this has been discussed already but I can't find the thread, so pardon me if it is the case...

      Is it possible to create an output mod slot in a C++ node?
      or is the only solution to use a global cable?

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

      HISEnbergH 1 Reply Last reply Reply Quote 0
      • 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

                              11

                              Online

                              2.0k

                              Users

                              12.7k

                              Topics

                              110.5k

                              Posts