HISE Logo Forum
    • Categories
    • Register
    • Login

    Polyphonic Custom Filters (scriptnode) how?

    Scheduled Pinned Locked Moved Unsolved ScriptNode
    27 Posts 4 Posters 1.1k 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.
    • A
      aaronventure
      last edited by

      Your post is a little bit vague for this niche part of HISE, which I think is why you're not getting any replies. You're implying understanding of the workflow, and you're the one who made the tutorials for it, up until which point it was mostly black magic to a lot of people here.

      Where does this code go? How would one test this out? Can I just stick it in SNEX?

      When importing a RNBO patch, in the RNBO exporter you disable polyphony because HISE lets you tick it when creating a template. Is there something like that for C++?

      Christoph HartC griffinboyG 2 Replies Last reply Reply Quote 1
      • Christoph HartC
        Christoph Hart @aaronventure
        last edited by

        You need to wrap your single filter class into a PolyData container:

        struct GriffinBoyFilter
        {
            void process(float* data, int numSamples);
        };
        
        // in the node:
        template <int NV> struct node
        {
            PolyData<GriffinBoyFilter, NV> filters;
        };
        

        See:

        Link Preview Image
        HISE | Docs

        favicon

        (docs.hise.dev)

        This basically creates one filter per voice and automatically selects the one that is assigned to the currently rendered voice.

        griffinboyG 2 Replies Last reply Reply Quote 3
        • griffinboyG
          griffinboy @aaronventure
          last edited by

          @aaronventure

          Haha yeah whoops. Sorry.
          I assumed it would have to be a question for christoph, maybe I should've addressed him directly xD

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

            @Christoph-Hart

            Thank you I missed that in the docs!

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

              @Christoph-Hart

              Sorry, I'm still not having any luck!

              #pragma once
              #include <JuceHeader.h>
              #include "src/ScopedValue.h" 
              
              namespace project
              {
                  using namespace juce;
                  using namespace hise;
                  using namespace scriptnode;
              
                  template <int NV>
                  struct Moog_Filter : public data::base
                  {
                      SNEX_NODE(Moog_Filter);
              
                      struct MetadataClass
                      {
                          SN_NODE_ID("Moog_Filter");
                      };
              
                      // 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 1; } // Mono processing
              
                      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;
              
                      // Define AudioEffect class here
                      class AudioEffect
                      {
                      public:
                          AudioEffect() = default;
              
                          void prepare(float sampleRate)
                          {
                              fs = sampleRate;
                              reset();
                          }
              
                          void reset()
                          {
                              // Initialize state variables to zero
                              s1 = s2 = s3 = s4 = 0.0f;
                          }
              
                          void updateCoefficients(float fc, float resonanceValue)
                          {
                              // Limit cutoff frequency to Nyquist frequency
                              float fcClamped = std::clamp(fc, 20.0f, 0.49f * fs);
              
                              // Pre-warp the cutoff frequency
                              float g = std::tan(MathConstants<float>::pi * fcClamped / fs);
              
                              // Set the resonance parameter (k) for the Moog ladder filter
                              k = 4.0f * resonanceValue; // Max resonance at k = 4
              
                              // Compute filter coefficients
                              G = g / (1.0f + g);
              
                              // Calculate gain compensation
                              float dbGain = quadratic_curve(resonanceValue, a, b, c);
                              gainCompensation = std::pow(10.0f, dbGain / 20.0f);
                          }
              
                          void process(float* samples, int numSamples)
                          {
                              for (int i = 0; i < numSamples; ++i)
                              {
                                  samples[i] = processSample(samples[i]);
                              }
                          }
              
                      private:
                          // Moog ladder filter processing variables
                          float fs = 44100.0f; // Sample rate
                          float k = 0.0f;      // Resonance parameter
                          float G = 0.0f;      // Filter coefficient
                          float gainCompensation = 1.0f; // Gain compensation factor
              
                          // State variables for each stage
                          float s1 = 0.0f;
                          float s2 = 0.0f;
                          float s3 = 0.0f;
                          float s4 = 0.0f;
              
                          // Curve fit parameters
                          static constexpr float a = -8.0f;
                          static constexpr float b = 18.2f;
                          static constexpr float c = 1.5f;
              
                          inline float processSample(float input)
                          {
                              // Input with feedback
                              float u = input - k * s4;
              
                              // Four cascaded one-pole filters
                              s1 = G * (u - s1) + s1;
                              s2 = G * (s1 - s2) + s2;
                              s3 = G * (s2 - s3) + s3;
                              s4 = G * (s3 - s4) + s4;
              
                              // Output is the last stage, apply gain compensation
                              return s4 * gainCompensation;
                          }
              
                          static float quadratic_curve(float x, float a, float b, float c)
                          {
                              return a * x * x + b * x + c;
                          }
                      };
              
                      // Wrap AudioEffect in a PolyData container for polyphonic handling
                      PolyData<AudioEffect, NV> leftChannelEffect;
              
                      // Prepare: Called on init, and when sample rate changes
                      void prepare(PrepareSpecs specs)
                      {
                          // Forward prepare call to PolyData container
                          leftChannelEffect.prepare(specs);
                      }
              
                      // Reset: Called when the plugin is reloaded
                      void reset()
                      {
                          // Use iterator to reset each voice
                          for (auto& effect : leftChannelEffect)
                          {
                              effect.reset();
                          }
                      }
              
                      // Process: Audio blocks enter the script here
                      template <typename ProcessDataType>
                      void process(ProcessDataType& data)
                      {
                          auto& fixData = data.template as<ProcessData<1>>(); // Process only one channel
                          auto audioBlock = fixData.toAudioBlock();
              
                          // Get pointer to left channel data
                          auto* leftChannelData = audioBlock.getChannelPointer(0);
              
                          // Correctly get the number of samples
                          int numSamples = (int)data.getNumSamples();
              
                          // Process each voice in the PolyData container
                          for (auto& effect : leftChannelEffect)
                          {
                              effect.process(leftChannelData, numSamples);
                          }
                      }
              
                      template <int P>
                      void setParameter(double v)
                      {
                          // Use iterator to update coefficients for each voice
                          for (auto& effect : leftChannelEffect)
                          {
                              if (P == 0)
                              {
                                  cutoffFrequency = static_cast<float>(v);
                                  effect.updateCoefficients(cutoffFrequency, resonance);
                              }
                              else if (P == 1)
                              {
                                  resonance = static_cast<float>(v);
                                  effect.updateCoefficients(cutoffFrequency, resonance);
                              }
                          }
                      }
              
                      // Create parameters on the GUI
                      void createParameters(ParameterDataList& data)
                      {
                          {
                              parameter::data p("Cutoff Frequency", { 20.0, 20000.0, 1.0 }); registerCallback<0>(p);
                              p.setDefaultValue(1000.0);
                              data.add(std::move(p));
                          }
                          {
                              parameter::data p("Resonance", { 0.0, 1.0, 0.01 }); registerCallback<1>(p);
                              p.setDefaultValue(0.0);
                              data.add(std::move(p));
                          }
                      }
              
                      // Interact with external data (e.g., an external buffer)
                      void setExternalData(const ExternalData& data, int index) {}
              
                      // Handle HISE events: Process MIDI or other events
                      void handleHiseEvent(HiseEvent& e) {}
              
                      // processFrame: Needed for compiler, does nothing
                      template <typename FrameDataType>
                      void processFrame(FrameDataType& data) {}
              
                  private:
                      // Filter parameters
                      float cutoffFrequency = 1000.0f;
                      float resonance = 0.0f;
                  };
              }
              

              The audio samples aren't being affected at all. I don't think I understand how to use polydata.

              Looking at it myself I've been confused, and as you can see, AI hasn't been particularly useful either : (

              ustkU 2 Replies Last reply Reply Quote 0
              • griffinboyG griffinboy has marked this topic as unsolved on
              • ustkU
                ustk @griffinboy
                last edited by

                @griffinboy Might be silly because I'm sure you thought about this 100 times already, but are you sure you're in a Polyphonic Script FX context?

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

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

                  @ustk

                  Yep haha, thank you though.

                  I wish I didn't have to ask such questions, but I am slow to understand the Hise source code : (

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

                    @griffinboy Yep tested and working here

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

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

                      @griffinboy Don't be harsh on yourself, no one has publicly advanced on the C++ topic as you did!
                      I wouldn't have made my first steps without you! 😉

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

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

                        @ustk

                        Ah my code will probably not work when run it has a dependency, I should probably include it in this post.

                        I might rewrite this post to make it easier for christoph to help me out 😆
                        I just need to figure out how to create an instance of a class for each voice, and process audio through that

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

                          @griffinboy said in Polyphonic Custom Filters (scriptnode) how?:

                          Ah my code will probably not work when run it has a dependency

                          Well I just commented out the dependancy and it kinda works... 🤷

                          I just need to figure out how to create an instance of a class for each voice

                          But that's what the wrapper does with NV, isn't it?

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

                          griffinboyG Christoph HartC 2 Replies Last reply Reply Quote 0
                          • griffinboyG
                            griffinboy @ustk
                            last edited by

                            @ustk

                            I don't believe so, or at least not for my node. It was not producing any audible effect, wheras inside a regular scriptfx (non poly) it was working.

                            I have just realised that I may have solved this before, I wrote a pretty bad sampler a few months ago using ai, and had unwittingly used polydata, which I stole the usage of from the regular sampler inside of hise. I'm going to investigate that now.

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

                              Code looks OKish on the first glance, but you need to call updateCoefficients in your prepare callback too though so that it can initialize the coefficients when the sample rate is set (otherwise it might be zero because the parameters might or might not be set before or after the call).

                              griffinboyG ustkU 2 Replies Last reply Reply Quote 0
                              • griffinboyG
                                griffinboy @Christoph Hart
                                last edited by

                                @Christoph-Hart

                                I'll try with a more simple effect.

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

                                  @griffinboy Well in my video above it works, and it is just your pasted code, node inserted in a poly FX

                                  @Christoph-Hart noticed that the coeff weren't updated as they should but I got lucky having the filter working straight ahead

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

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

                                    @ustk

                                    Okay maybe I should update my version of hise.
                                    I'm very surprised that everything seems to be in order lol.

                                    For me it still only works when used in monophonic.

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

                                      @Christoph-Hart Jus noticed that when two+ notes are held, changing the resonance makes the freq to be updated to last note value for all notes. What could be done to prevent this?

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

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

                                        @griffinboy Strange indeed...

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

                                        griffinboyG Christoph HartC 2 Replies Last reply Reply Quote 0
                                        • griffinboyG
                                          griffinboy @ustk
                                          last edited by

                                          @ustk

                                          The fact that it works without dependencies means that the ai has removed some of my optimisations, I'll release a proper version of this filter when it's done lol.

                                          I'm amused that it's (somewhat) working for you 😆

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

                                            Yup it works here too, but you definitely need to put the function call into prepare().

                                            us noticed that when two+ notes are held, changing the resonance makes the freq to be updated to last note value for all notes.

                                            That's intended - any UI interaction will be applied to all voices. If you want to use different values, you have to modulate it, then it will be applied for each voice:

                                            HiseSnippet 1763.3oc2Y0zSiaDFdbfIrjkVs6psRcukC8.aEBk.rvh1CD9JaQcCDQnnsmPC1SHSi8Lt1N.YqpTkZOr2548V+azSs+ApTk5uf9Of+AsuiGarcrARLeraaPBxLuy63G+9wy7XSSGgN00U3fzJsWeaJRaJbq9buNq2gv3ns1.o8w3FDWOpSY0Tq02l35RMPZZi8R4DZSNNx+yYqrFwjv0oQSgP6KX5zWwrXdQy1r1WxLMqSLn6wrhs5EpsktfutvTzCvyX3JHahdWxQzsIxkU.i9BhaGj1miq7rEozklusgAY94MVbwEW94KuvxUIsqr.4vpKt7BysvRy87kZizJtoAyS3zxi3QcgMcMgQ+VcDmvUWf8YtrCMoxAUQsfqrZZz5cXlFMCCNtHjFtYTnZLUn5w3FLC14yGExdfugxQdDOnoU3xfT0Q.RZwfz3JH8PbKcGlsWjEIdtOdKNjAaSfbSbnnVKR62wqKfEv8l0hzkV2AFbtCSuXkJyTF90SeQ6dbcOlfWVv2V3Q2gO8SK8cklrz2Wp7flZ2NSaxKiivzj5joYY4fyk43z7dVGRclo7wDydzyWHb6mLlVb3ho5p65XKTv2hy71wlFLttvzPFqjeOcF.ED1fu8UasAwiHSJAyAqyl53wjvQaC5wPafJEMIdCpaWOgMzHjJ+AUNBidlDujkSxFs.CP7HQNTln3tLu9waDGgZrJWZM1vBwGhax7z6jMFKjAFgH0sAFC5L+H7lsaS08h.3335uNusgUF41vRAsgv0TdwmJXX45utZlMfuEGUra6PsINz8DMMI8m1kXYaR2Ev3LkOzTn2sE6MzzcH1JHrlbESq2gv4TS27zHU7FhbBsqnmGieTChmC6Tf9b6dVs.dcc55AnClSqfrGRMthbrrtnEka3O3efOAFqJGqEXrZnwXseaS8NQ3z0OcD7cj1DpXuqeP9f1mBvm53JKN0l.WYV3GzpllhSVWXYyBJagjh+bMEl8s6H3Lc4TpUDB8UsD8fqZ.9gyj1ivLk04s54BLGF6vaAK1+fPYc11BC3aSTmnCA19MIdcj8KRtGn3j5Lq94EoIvZ75D0AfILKSJMINfAfn1UkFjWoviOUAS.3VxHj7vSXa.69MLQ9IKeCGANoNsHZlFL99xJDvjLhgZPN87wUgwIFrAsMomoWh474C4T+xufd0PZQUmibDvKTPc+uk69Rq5DyvsFhwqIDcsHpragvAAtLAFJt61Ob0OB6xNZNKgwKHFFuvlR5hPC3QIrcOGZ4cb0CcZJrvUGTjHozt9L4SFRAoZt8US4mceRvLbHKT1eZpKyk5T8RzT8mCqlJ6gVSUHmyL3qAuv3CGuPxyshs9LnQK9dPf0MygeuGOeNwgOSFhwVL4gFaxOlZBkt9X7Q3ftyvYSV00PvEgzcQEZ6RAx6iNRxLDg8LugV0yCjpGMyiqsK0jRbiUI9Y0dEP2QbZ3SGlqXwnqGNy70mhUvsrrUo7+c0UM1+q0UMrgn6g27TOGR0O.ydAPat6Rn8ghtqmDHVQdZ2AtwOsa3zbU3Zn4JkLuPQXEGBQXWLvSqH6hW6UIEKO5fBjuozwA2LpkFS6Rp6NG5rwLmkhxXlGEHKMAGwbZnToMXt1viqrVOfdPwEmXF48E2fdZXEzlfHCC3Aq8cOrpJgpzhwTkNQjJzHZtHkngy8fZs7n1xmOJdmUqtzSTwkDGhAgthX+ihFXWNakjxXithnKGRKTKEjd6eMHj9k28teMMjp8iV+wJJo20cneaOnNt+.35G5VKSbAyeE35rURGppLhgpo7wU4ck8qCa.6rUPidND.wHlCeIPXdmCIfP7BgzD3lcjBeRs04rt5l.SE8UnNBgoXLLkvviSonGSvu73XrmmuhzTLwb+1jSTKFhKhg3ocRzdeEanugzfLviq.fimfALRS1dDmindtnDOravYNaYHUM0PHN5f5LSYN97rszzivq2ySztc4n99acp0wtj5Me9oT0a094giGKFeQSpCSX3+rno1qrq595Z2AMB2G+JgvFTY438AWCZ93wjkhwp8m.aAqmgtPgNJyoaABcbjTAD18UHABxfrHsTqrPvcKMAb8fGQc.USSgsf+NqxRlXz2xUfwBIhRW3qa6V6DH0FjiB7AplRxbkHP8IXaGw2.bcyFeIoiXI2h7G2JDKtcubp95JDSjAYbxcrvukM2EvosJ3IT4n9+cbSPXT6ueyOsxkQjsK0Uv8e0f4LOmXPr2wZ9dCn2EO2oEQ2Qbft5UUJKJtm+L.m.2uVaRbC43xUQGO3stEyfcftdxsJkiykWGmOuNtPdc7Y40wEyqiKkWGe9U6n704FzBI0NAcHM2TofQaSN4PSEiB5eoxNyhG
                                            
                                            griffinboyG 2 Replies Last reply Reply Quote 1
                                            • First post
                                              Last post

                                            23

                                            Online

                                            1.7k

                                            Users

                                            11.9k

                                            Topics

                                            103.6k

                                            Posts