HISE Logo Forum
    • Categories
    • Register
    • Login

    template or tutorial for custom c++ scriptnode?

    Scheduled Pinned Locked Moved ScriptNode
    38 Posts 8 Posters 3.8k 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.
    • MorphoiceM
      Morphoice @ustk
      last edited by

      @ustk aye! I did this in C++ not SNEX though. But I agree it looks more correct this way, although it didn't throw a tantrum without. C++ seems to be a bit forgiving there, which is not at all what I heard about it lol

      float val = samples[i];
      int max = 255;
      float valmu  = sgn(val) * log( 1.0f + max * abs(val) ) / log( 1.0f + max);
      float valmu8 = valmu - fmodf(valmu, 1.0f / max);
      float valex = sgn(valmu8) * (1.0f / max) * ( pow(max + 1.0f, abs(valmu8)) - 1.0f );                   
      

      https://instagram.com/morphoice - 80s inspired Synthwave Music, Arcade & Gameboy homebrew!

      MorphoiceM 1 Reply Last reply Reply Quote 1
      • MorphoiceM
        Morphoice @Morphoice
        last edited by

        @Christoph-Hart is there a way to get the custom c++ scriptnode to only process data if there is any sound? as this is intended for one-shots but constantly does the encoding/decoding and a couple of those nodes produce quite a fair amount of cpu

        https://instagram.com/morphoice - 80s inspired Synthwave Music, Arcade & Gameboy homebrew!

        ustkU griffinboyG 2 Replies Last reply Reply Quote 0
        • ustkU
          ustk @Morphoice
          last edited by ustk

          @Morphoice I can think of two ways:

          • either build a detector that is waiting for a certain value to pass a threshold, then if it does, do your stuff
          • tell the node a signal is coming with a modulated parameter, which will move the detection part to the graph

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

          MorphoiceM 1 Reply Last reply Reply Quote 1
          • MorphoiceM
            Morphoice @ustk
            last edited by

            @ustk are all nodes generally on? I've read about a hasTail parameter that stops processing when the note is done etc. probably in my case it needs to work only a few ms after a note was fired

            https://instagram.com/morphoice - 80s inspired Synthwave Music, Arcade & Gameboy homebrew!

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

              @Morphoice I reckon hasTail is for the case where DAWs cut the plugin processing between the clips to save CPU on big projects. hastTail allows your plugin to still read the buffers until a minimum threshold has been reached before effectively cutting the sound (i.e. for reverbs, etc...)

              If your node is optimised, you shouldn't need to bypass it. If the heavy processing is done then you can just do nothing in the process callback to save CPU using just one branching (if statement)

              I don't know how your system is built, but if your sound is triggered with MIDI you can use midi events in C++ node with handleHiseEvent, but if it's just reacting to audio then...

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

              MorphoiceM 1 Reply Last reply Reply Quote 1
              • MorphoiceM
                Morphoice @ustk
                last edited by

                @ustk don't VST3 plugins in general only waste cpu when they detect the presence of an audio signal?

                https://instagram.com/morphoice - 80s inspired Synthwave Music, Arcade & Gameboy homebrew!

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

                  @Morphoice might be true but you canβ€˜t rely on every host to implement this, also there are other plugin formats which you need to factor in.

                  S 1 Reply Last reply Reply Quote 1
                  • S
                    sletz @Christoph Hart
                    last edited by sletz

                    In the meantime, written in Faust: https://faustlibraries.grame.fr/libs/basics/#bamulaw_bitcrusher
                    and the code here https://github.com/grame-cncm/faustlibraries/blob/a222717339e5f528787080e5e5438ffef8da22a9/basics.lib#L2429 and https://github.com/grame-cncm/faustlibraries/blob/a222717339e5f528787080e5e5438ffef8da22a9/basics.lib#L2371

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

                      @Morphoice

                      Yes there are a few ways. The best is probably to use the built in flags at the top of the c++ node. But you can do custom detection too, which I sometimes use if I need specific behaviour.

                      You'll probably want to optimise the nodes in the first place, I see you've got all the random example scripts (smooth gain ect) that aren't super necessary for your use case. I can give you a more simple template if you want one.

                      I intend to go back and cover this c++ node stuff better and in more detail but I'm short on time currently. I will be releasing a proper template in the future, with a lightweight library to go along with it, containing efficient approximations of math functions (rather useful for your bitcrusher script), automatic lookuptable creation, and hopefully some tools for SIMD.

                      And yes - you are progressing really quickly! I get the feeling that you are going to do really well. I don't think I'll have a plugin released until at least two years time 🀦 Too much perfectionism

                      MorphoiceM 1 Reply Last reply Reply Quote 1
                      • MorphoiceM
                        Morphoice @griffinboy
                        last edited by

                        @griffinboy thanks man! a simpler template would be awesome, I also figured It would make sense to do the calculations only if samples[i] isnt zero, that should take most of the load in my case.

                        https://instagram.com/morphoice - 80s inspired Synthwave Music, Arcade & Gameboy homebrew!

                        griffinboyG 2 Replies Last reply Reply Quote 0
                        • griffinboyG
                          griffinboy @Morphoice
                          last edited by

                          @Morphoice

                          Yep that makes sense, since you don't need to process the zero crossings for your effect.

                          Link Preview Image
                          C++ Node Templates – Google Drive

                          favicon

                          Google Drive (drive.google.com)

                          Here's a more up to date version of the template. You'll have to wait until I've got time, for me to release the official version of this, which will have many more improvements

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

                            @Morphoice Did a small fix there was an error in the template

                            Link Preview Image
                            C++ Node Templates – Google Drive

                            favicon

                            Google Drive (drive.google.com)

                            MorphoiceM LindonL 3 Replies Last reply Reply Quote 0
                            • MorphoiceM
                              Morphoice @griffinboy
                              last edited by

                              @griffinboy said in template or tutorial for custom c++ scriptnode?:

                              Link Preview Image
                              C++ Node Templates – Google Drive

                              favicon

                              Google Drive (drive.google.com)

                              thanks so much! I'll use this wisely to make new nodes ;))

                              https://instagram.com/morphoice - 80s inspired Synthwave Music, Arcade & Gameboy homebrew!

                              1 Reply Last reply Reply Quote 0
                              • MorphoiceM
                                Morphoice @griffinboy
                                last edited by

                                @griffinboy btw just tried compiling the node on PC but that didnt work at all it's throwing a ton of errors. you wont happen to have any idea what I did wrong?

                                // ==========================| muLawBitCrusher v1.0 by Morphoice |==========================
                                
                                #pragma once
                                #include <JuceHeader.h>
                                #include <cmath>
                                
                                
                                namespace project
                                {
                                    using namespace juce;
                                    using namespace hise;
                                    using namespace scriptnode;
                                
                                    template <typename T> int sgn(T val) {
                                        return (T(0) < val) - (val < T(0));
                                    }
                                
                                    template <int NV>
                                    // --- Replace with Name
                                    struct muLawBitCrusher : public data::base
                                    {   // ------ Replace with Name
                                        SNEX_NODE(muLawBitCrusher);
                                
                                        struct MetadataClass
                                        { // --------- Replace with "Name"
                                            SN_NODE_ID("muLawBitCrusher");
                                        };
                                
                                        // ==========================| 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)
                                                {
                                                    float val = samples[i];
                                                    
                                                    if (val!=0) {
                                                        
                                                        // mu-law compress signal
                                                        int max = 255;
                                                        float valmu  = sgn(val) * log( 1.0f + max * abs(val) ) / log( 1.0f + max);
                                                        
                                                        // Quantize to 8 bits
                                                        float valmu8 = valmu - fmodf(valmu, 1.0f / max);
                                                        
                                                        // mu-law expand signal
                                                        float valex = sgn(valmu8) * (1.0f / max) * ( pow(max + 1.0f, abs(valmu8)) - 1.0f );
                                                        
                                                        // Apply Demo Gain
                                                        samples[i] = valex * smoothGain.advance();
                                                        
                                                    }
                                                    /*
                                                     float rem = fmodf(val,1/max);
                                                     
                                                     // Linear quantized to 8 bits
                                                     float val8 = val - rem;
                                
                                                     */
                                                    
                                                }
                                            }
                                
                                            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("LeftGain", { 0.1, 2.0, 0.01 }); registerCallback<0>(p);
                                                p.setDefaultValue(1.0);
                                                data.add(std::move(p));
                                            }
                                            {
                                                parameter::data p("RightGain", { 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;
                                    };
                                }
                                
                                
                                

                                what works on mac doesn't on PC

                                error C3203: 'muLawBitCrusher':
                                unspecialized class template can't be used as a template argument for template parameter 'T', expected a real type
                                

                                https://instagram.com/morphoice - 80s inspired Synthwave Music, Arcade & Gameboy homebrew!

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

                                  @Morphoice

                                  it compiles on my end.
                                  Did you make sure to name the .h file as follows:

                                  muLawBitCrusher.h
                                  

                                  The c++ file always needs to share the same name as the node.
                                  Also by the way, your effect has a heck ton of aliasing.

                                  Edit: *facepalm, aliasing is the point, please ignore me lol

                                  MorphoiceM 1 Reply Last reply Reply Quote 0
                                  • MorphoiceM
                                    Morphoice @griffinboy
                                    last edited by

                                    @griffinboy ;-))) yes it's named correctly, the VS compiler seems to take offense of my sgn function I got off the webs which is weird as it works fine on mac

                                    template <typename T> int sgn(T val) {
                                            return (T(0) < val) - (val < T(0));
                                        }
                                    

                                    https://instagram.com/morphoice - 80s inspired Synthwave Music, Arcade & Gameboy homebrew!

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

                                      @Morphoice

                                      Yeah it works on my machine... try a fresh hise project.
                                      You could also always try recompiling the latest hise.
                                      If those two things don't fix it, then it's a tricky one

                                      MorphoiceM 2 Replies Last reply Reply Quote 0
                                      • MorphoiceM
                                        Morphoice @griffinboy
                                        last edited by Morphoice

                                        @griffinboy i'm getting rid of that template thing for that stupid sign function, which is a syntax beyond my c++ knowledge

                                        this should work too, at least on mac it compiles fine, gotta try on the pc later

                                                                // mu-law compress signal
                                                                int max      = 255;
                                                                int sign     = (0 < val) - (val < 0);
                                                                float valmu  = sign * log( 1.0f + max * abs(val) ) / log( 1.0f + max);
                                                                
                                                                // Quantize to 8 bits
                                                                float valmu8 = valmu - fmodf(valmu, 1.0f / max);
                                                                
                                                                // mu-law expand signal
                                                                float valex = sign * (1.0f / max) * ( pow(max + 1.0f, abs(valmu8)) - 1.0f );
                                        

                                        https://instagram.com/morphoice - 80s inspired Synthwave Music, Arcade & Gameboy homebrew!

                                        1 Reply Last reply Reply Quote 0
                                        • MorphoiceM
                                          Morphoice @griffinboy
                                          last edited by

                                          @griffinboy on second thought sign probably only needs to calculate once and should be the same on compressing and expanding a sample so even more calculations can be avoided

                                          https://instagram.com/morphoice - 80s inspired Synthwave Music, Arcade & Gameboy homebrew!

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

                                            @griffinboy said in template or tutorial for custom c++ scriptnode?:

                                            @Morphoice Did a small fix there was an error in the template

                                            Link Preview Image
                                            C++ Node Templates – Google Drive

                                            favicon

                                            Google Drive (drive.google.com)

                                            Sadly this is pretty unusable - as it blocks chrome from looking at it if adblockers are turned on...

                                            HISE Development for hire.
                                            www.channelrobot.com

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

                                            13

                                            Online

                                            1.8k

                                            Users

                                            12.0k

                                            Topics

                                            104.5k

                                            Posts