HISE Logo Forum
    • Categories
    • Register
    • Login

    [Tutorial] How to create a c++ custom node

    Scheduled Pinned Locked Moved Blog Entries
    26 Posts 12 Posters 1.3k 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

      How to create a custom node, as promised.
      I apologise in advance for the awful quality video (quite dry and boring)! But the good news is that I have some nicer quality ones in the pipeline. However I thought I should get this out as soon as possible since some people were waiting on it.
      Hope it helps.

      Windows only, I'll do a mac tutorial later.

      Template v1.1:

      // ==========================| NodeTemplate v1.0 : by Griffinboy |==========================
      
      #pragma once
      #include <JuceHeader.h>
      
      namespace project
      {
          using namespace juce;
          using namespace hise;
          using namespace scriptnode;
      
          template <int NV>
          // --- Replace with Name 
          struct ExternalNodeTemplate : public data::base
          {   // ------ Replace with Name 
              SNEX_NODE(ExternalNodeTemplate);
      
              struct MetadataClass
              { // --------- Replace with "Name"
                  SN_NODE_ID("ExternalNodeTemplate");
              };
      
              // ==========================| 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; }
      
              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;
      
              // ==========================| Global Variables |==========================
      
              // ==========================| Prepare |==========================
              // Called on init, and when sample rate changes
              void prepare(PrepareSpecs specs)
              {
                  float sampleRate = specs.sampleRate;
                  float numChannels = specs.numChannels;
      
                  leftChannelEffect.prepare(sampleRate, 5.0); // 5ms smoothing on the gain parameter
                  rightChannelEffect.prepare(sampleRate, 5.0);
              }
      
              // ==========================| Reset |==========================
              // Called when the plugin is reloaded
              void reset() {}
      
              // ==========================| Process |==========================
              // Blocks of audio enter the script here
              template <typename ProcessDataType>
              void process(ProcessDataType& data)
              {
                  // Convert the audio data to a fixed-channel format for efficient processing
                  auto& fixData = data.template as<ProcessData<getFixChannelAmount()>>();
      
      			// Get pointers to each channel's audio data
                  // Changes to these variables will now directly modify the original audio buffer
                  auto audioBlock = fixData.toAudioBlock();
                  auto* leftChannelData = audioBlock.getChannelPointer(0);
                  auto* rightChannelData = audioBlock.getChannelPointer(1);
      
                  // Get the number of samples (one channel) for this block
                  int numSamples = data.getNumSamples();
      
                  // Pass each channel's audio to the appropriate AudioEffect class
                  leftChannelEffect.process(leftChannelData, numSamples);
                  rightChannelEffect.process(rightChannelData, numSamples);
              }
      
              // ==========================| AudioEffect Class |==========================
              class AudioEffect
              {
              public:
                  AudioEffect(float initialGain = 1.0f)
                  {
                      smoothGain.set(initialGain); // Initialize sfloat with initial gain
                  }
      
                  void prepare(double sampleRate, double timeInMilliseconds)
                  {
                      smoothGain.prepare(sampleRate, timeInMilliseconds);
                  }
      
                  void process(float* samples, int numSamples)
                  {
                      for (int i = 0; i < numSamples; ++i)
                      {
                          samples[i] *= smoothGain.advance(); // Apply the gain using the smoothed parameter
                      }
                  }
      
                  void updateGain(float newGain)
                  {
                      smoothGain.set(newGain);
                  }
      
              private:
                  sfloat smoothGain; // Declare smoothGain variable, using sfloat type for smoothing
              };
      
              // ==========================| Set Parameter |==========================
              template <int P>
              void setParameter(double v)
              {
                  if (P == 0)
                      leftChannelEffect.updateGain(static_cast<float>(v)); // Update gain for left channel
                  else if (P == 1)
                      rightChannelEffect.updateGain(static_cast<float>(v)); // Update gain for right channel
              }
      
              // ==========================| Create Parameters |==========================
              void createParameters(ParameterDataList& data)
              {
      
                  {   //                             { min, max,  step }                 { id } 
                      parameter::data p("Left Gain", { 0.1, 2.0, 0.01 }); registerCallback<0>(p);
                      p.setDefaultValue(1.0);
                      data.add(std::move(p));
                  }
                  {
                      parameter::data p("Right Gain", { 0.1, 2.0, 0.01 });
                      registerCallback<1>(p);
                      p.setDefaultValue(1.0);
                      data.add(std::move(p));
                  }
              }
      
              // ==========================| External Data |==========================
              void setExternalData(const ExternalData& data, int index) {}
      
              // ==========================| Handle HISE Event |==========================
              void handleHiseEvent(HiseEvent& e) {}
      
              // ==========================| Modulation Slot |==========================
              // ( first enable isModNode() at the top of script ) 
              /*
                  ModValue modValue;
                  int handleModulation(double& value)
                  {
                      return modValue.getChangedValue(value);
                  }
                  // Creates callback so that altering the 'modValue' var elsewhere will now update the mod slot
                  modValue.setModValue(0);
              */
      
              // processFrame: Needed for compiler, does nothing
              template <typename FrameDataType>
              void processFrame(FrameDataType& data) {}
      
          private:
              AudioEffect leftChannelEffect;
              AudioEffect rightChannelEffect;
          };
      }
      
      

      14434c25-0fb6-4abd-bcc3-10cdc249cdd1-image.png

      Christoph HartC orangeO griffinboyG C 4 Replies Last reply Reply Quote 14
      • Christoph HartC
        Christoph Hart @griffinboy
        last edited by

        @griffinboy Great stuff, a really clear introduction to the workflow that I've had in mind for advanced DSP stuff.

        I've got 2 suggestions, one cosmetically and one critical error in your template:

        1. For the development compile-test cycle I would recommend using the hardcoded master FX and a saved .hip preset that you can recall with one click, then you don't have to perform so many actions to get the node running when you start debugging. Also the note icon next to the keyboard on the top plays a single note (right click to enable note toggle to simulate your sustain pedal usage of the virtual keyboard). Saves you a few clicks...
        2. The blockSize variable must not be taken from the processing specs. It's a common misconception that will blowup your plugin in some hosts (FL Studio being the most naughty one in this regard). There is a "maximum" number of samples that you can expect to get (which is the blockSize from the prepare call and should be used to setup buffers and other data structures) and the actual samples that you have to process, which you can query from the ProcessData structure with the getNumSamples() method. That value can be lower on certain occasions (either because the DAW splits up the processing at loop points or because it's FL Studio and they do whatever they want), so if you assume a block size of the maximum for every process call you'll get a read access violation and a hard crash pretty reliably.

        https://docs.hise.dev/scriptnode/snex_api/data_structures/preparespecs.html
        https://docs.hise.dev/scriptnode/snex_api/data_structures/processdata.html

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

          @Christoph-Hart

          Whoops!
          Thank you for that!

          I shall amend this and be re-uploading the video anyway, because I found it to be very dry haha.
          I shall have to get used to talking on video, doing VO is not something I have experience with!

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

            @griffinboy don't be too harsh on yourself, the presentation and pace is top notch and you're talking about a super nerdy subject here so being too dry is a non-issue.

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

              @Christoph-Hart
              <3

              1 Reply Last reply Reply Quote 0
              • orangeO
                orange @griffinboy
                last edited by

                @griffinboy Thanks for this useful and fluent tutorial, I'm sure it will be very helpful for lot's of developers here.

                develop Branch / XCode 13.1
                macOS Monterey / M1 Max

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

                  @griffinboy

                  Update: Fixed the template for FL Studio buffer size compatibility

                  ulrikU LindonL 2 Replies Last reply Reply Quote 0
                  • ulrikU
                    ulrik @griffinboy
                    last edited by

                    @griffinboy Thanks a lot for this tutorial! It's much appreciated!

                    Hise Develop branch
                    MacOs 15.3.1, Xcode 16.2
                    http://musikboden.se

                    1 Reply Last reply Reply Quote 0
                    • LindonL
                      Lindon @griffinboy
                      last edited by

                      @griffinboy well done - !!

                      HISE Development for hire.
                      www.channelrobot.com

                      1 Reply Last reply Reply Quote 0
                      • DabDabD
                        DabDab
                        last edited by DabDab

                        @griffinboy Thank you I need more tutorials on this topic. It is another HISE learning Youtube Channel / Learning path for Audio Plugins developers.

                        Bollywood Music Producer and Trance Producer.

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

                          @DabDab

                          The next video I release will be a reupload of the last one. I want to clean it up and include clearer explanations of how audio data travels through a plugin. I also have a general hise startup guide, but this is aimed at complete beginners.

                          What would you like to see next in terms of DSP though?
                          I said in the video that I would be covering filters, but some of that stuff is horribly complex. I'd rather cover another topic before analog modelling haha.

                          DabDabD ShelestS 2 Replies Last reply Reply Quote 0
                          • C
                            Consint @griffinboy
                            last edited by

                            @griffinboy Many thanks for that! Your tutorial is really very helpful. Somewhere in the forum you mentioned the Chow Tape integration. I would really appreciate a tutorial on this.

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

                              @Consint

                              Okay I'll put that on the list.
                              Tape is fun.

                              yes, what I actually ended up doing was creating new dsp, heavily based on Chow's work.
                              Essentially porting parts of his work to Scriptnode.

                              I'll put that on the List.
                              External library integration is also on my Tutorial to-do list if that's what you were getting at. But I can certainly cover tape. Distortion was a topic I had thought of, but it's a very deep rabbit hole.

                              1 Reply Last reply Reply Quote 2
                              • DabDabD
                                DabDab @griffinboy
                                last edited by DabDab

                                @griffinboy said in [Tutorial] How to create a c++ custom node:

                                @DabDab
                                What would you like to see next in terms of DSP though?

                                @griffinboy I am pretty much interested in Hyper SAW / Super Saw Oscillator design. HiSE default synthesizer Group and Waveform Generator SuperSaw / Unison is horrible. If I use Sampler in Synthesizer Group for HyperSaw/SuperSaw, it is weird flanged. Synthesizer group also the same. So I guess only C++ custom OSC design can be the remedy.

                                Videos for Good Osc , Hyper Saw and Good Filter with Envelop design.
                                https://youtu.be/-9bdn5S90j0?t=206
                                ** However I didn't try FAUST and RNBO for Custom OSC SAW specially for Unisono.

                                Bollywood Music Producer and Trance Producer.

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

                                  If I use Sampler in Synthesizer Group for HyperSaw/SuperSaw, it is weird flanged. Synthesizer group also the same.

                                  It flanges because all unisono voices start at the same time. In order to fix this, you need to add a little bit of sample start modulation (a few thousand samples), then the unison sounds much better.

                                  DabDabD 1 Reply Last reply Reply Quote 1
                                  • griffinboyG
                                    griffinboy @DabDab
                                    last edited by

                                    @DabDab

                                    I'm not an expert in Synthesis.
                                    I've only just started dipping into it.
                                    I'm looking at all the different methods for saw generation (With low to no aliasing, in mind) I'm likely to look at this topic after covering some basic distortion stuff., I'm still doing my research.

                                    Thanks for the suggestion.

                                    Matt_SFM 1 Reply Last reply Reply Quote 1
                                    • DabDabD
                                      DabDab @Christoph Hart
                                      last edited by

                                      @Christoph-Hart Great. Will try it.

                                      Bollywood Music Producer and Trance Producer.

                                      1 Reply Last reply Reply Quote 0
                                      • C
                                        ccbl
                                        last edited by

                                        Nice! Following.

                                        1 Reply Last reply Reply Quote 0
                                        • Matt_SFM
                                          Matt_SF @griffinboy
                                          last edited by

                                          @griffinboy Well done! Thank you for your time and your work, it's much appreciated :)
                                          I'll soon dive into custom nodes so this will help me a lot!

                                          Develop branch
                                          Win10 & VS17 / Ventura & Xcode 14. 3

                                          1 Reply Last reply Reply Quote 0
                                          • ShelestS
                                            Shelest @griffinboy
                                            last edited by

                                            @griffinboy said in [Tutorial] How to create a c++ custom node:

                                            I also have a general hise startup guide, but this is aimed at complete beginners.

                                            And there are such people here! For example, it is doubly difficult for me, not only am I not a programmer, I also do not know English. But even in this situation, intuitively, from the picture, I try and learn. Such visual lessons really help to join the HISE family. Many thanks to all of you for this!

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

                                            47

                                            Online

                                            1.7k

                                            Users

                                            11.7k

                                            Topics

                                            101.8k

                                            Posts