HISE Logo Forum
    • Categories
    • Register
    • Login
    1. HISE
    2. iamlamprey
    • Profile
    • Following 0
    • Followers 1
    • Topics 18
    • Posts 64
    • Groups 0

    iamlamprey

    @iamlamprey

    25
    Reputation
    11
    Profile views
    64
    Posts
    1
    Followers
    0
    Following
    Joined
    Last Online

    iamlamprey Unfollow Follow

    Best posts made by iamlamprey

    • [Tutorial] Real-Time Pitch Shifting with Rubberband

      Hello my old friends.

      Here's how to get realtime Pitch-Shifting working with the Rubberband Library.

      First, your project either needs to be GPL (open-source), or you need to pay the developers of Rubberband for a license (and Christoph!). Also this is just for Windows, I don't use or own a Mac so YMMV.

      First, there's some dependancy conflicts between JUCE and Rubberband. To fix, we have to open the file:

      HISE/hi_tools/simple_css/Renderer.h

      And replace EVERY instance of the Rectangle class with juce::Rectangle instead. I've tried the preprocessor flags and it didn't fix the problem, so I just ended up replacing everything manually. Don't use Find & Replace, there's some other "Rectangle" variables that you don't want to override, just go through manually and replace every Rectangle with the strictly type juce variant.

      I also replaced the copy variable in the Positioner class to rectangleCopy because I think this will also cause problems on Windows. Lines 333 and 336.

      Next, in your Projucer, add the preprocessor flag HI_ENABLE_CUSTOM_NODES=1.

      Okay, now we can build HISE. When it's done, open a project and hit Tools -> Create C++ Third Party Node Template. Name it anything.

      We'll also need to tell HISE where our Custom Nodes are stored, open HISE Settings -> Development -> Custom Node Path and point it to {*YourProjectName*}\DspNetworks\ThirdParty. Click "Save".

      We just created our Third Party node, but it won't do much good without the Rubberband Library.

      Navigate to that same folder in a File Browser, there should be a src folder, if there isn't, create it. Clone the Rubberband Repository into that folder.

      Open your custom node in any editor. Let's start with the includes, which go AFTER the JUCE one:

      // Original Code:
      #pragma once
      #include <JuceHeader.h>
      
      // What we're adding:
      #include "src/rubberband/single/RubberBandSingle.cpp"
      #include "src/rubberband/rubberband/RubberBandStretcher.h"
      

      Inside the primary node struct, declare a unique pointer to hold our RubberBandStretcher:

      // Original Code:
      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;
      
      // What we're adding:
      std::unique_ptr<RubberBand::RubberBandStretcher> rb;
      

      In the Prepare method, assign the RubberBandStretcher with the OptionProcessRealTime Option:

      void prepare(PrepareSpecs specs)
      {   
        rb = std::make_unique<RubberBand::RubberBandStretcher> (specs.sampleRate, specs.numChannels, RubberBand::RubberBandStretcher::Option::OptionProcessRealTime);
        rb->reset(); // not sure if this is necessary, but I don't think it hurts   
      }
      

      RubberBand uses Block-Based processing, not per-sample. So we'll use the process method, not the processFrame one:

      template <typename T> void process(T& data)
      {
        if (!rb) return; // safety check in case the rb object isn't constructed yet
      
        // "data" is given to us by HISE, it has multiple channels of audio values as Floats, as well as some utility functions:
        
        int numSamples = data.getNumSamples(); // the number of audio samples in the block
        auto ptrs = data.getRawDataPointers(); // RB expects Pointers, not just values
      
        rb->process(ptrs, numSamples, false); // This is the Pitch-Shifting Part
      
        // the "false" is just telling RB if we're using the last audio buffer, which we're not if it's realtime
      
        int available = rb->available(); // Checks to see if there is repitched audio available to return
      
        if (available >= numSamples)
        {
          // We have enough output, retrieve the repitched audio
          rb->retrieve(ptrs, numSamples);
        }     
      }
      

      Okay, we're almost there. We just need to tell the Pitch Shifter how much we want to shift the signal by. It uses a Frequency Ratio, so:

      1.0 = None
      0.5 = - Octave
      2.0 = + Octave
      

      If you'd prefer to have a Semitones-based control, I recommend using the conversion function in the ControlCallback of your UI element instead. It looks like this:

      // In the GUI Slider Control Callback
      // This assumes the Slider goes from -12 to +12, with a stepSize of 1.0
      
      inline function onknbPitchControl(component, value)
      {
        // bypass if semitones = 0 (save CPU & reduce latency)
        if (value == 0)
          pitchShifterFixed.setBypassed(true);
        else
          pitchShifterFixed.setBypassed(false);   
        
        // Calculate the FreqRatio from Semitone Values 
        local newPitch = Math.pow(2.0, value / 12.0);       
        pitchShifterFixed.setAttribute(pitchShifterFixed.FreqRatio, newPitch);
      };
      
      

      Either way, we need to connect our Custom Node's only Parameter to our RubberBand Object.

      In the createParameters method:

      void createParameters(ParameterDataList& data)
      {
        {
          // Create a parameter like this
          parameter::data p("FreqRatio", { 0.5, 2.0 }); // Between -1 Octave and +1 Octave
          // The template parameter (<0>) will be forwarded to setParameter<P>()
          registerCallback<0>(p);
          p.setDefaultValue(1.0);
          data.add(std::move(p));
        }
      }
      

      And finally, we connect the Parameter in the setParameters method:

      template <int P> void setParameter(double v)
      {
        if (P == 0) // This is our first paramater, if you add more, increment the index.
        {
          rb->setPitchScale(v); // passes the parameter value "v" to our pitch-shifter
        }
      }
      

      Okay, that's all the code we need.

      Now hit Export -> Compile DSP networks as dll.

      Assuming everything worked, you can now add a HardcodedMasterFX and load your pitch-shifter.

      Notes / Thoughts:

      1. I haven't tested this extensively, there might be other Includes that cause problems depending on your HISE installation.
      2. There's about 30-50ms Latency which is effectively unavoidable, the Pitch Shifter needs a window of a certain size in order to perform calculations.
      3. There might be other RubberBand::RubberBandStretcher::Option values that make it sound better for transients etc, I haven't tested them yet.
      4. Make sure your Parameter doesn't go to 0, as the Library uses it for dividing somewhere.

      Here's the full node, if you copy-paste it, make sure you rename it to whatever your node was or HISE won't find it:

      // ==================================| Third Party Node Template |==================================
      
      /*
      
      Don't forget, you have to replace every instance of Rectangle in HISE/hi_tools/simple_css/Renderer.h to juce::Rectangle
      
      And replace both "copy" variables in that same file with copyRectangle
      
      Then build HISE with HI_ENABLE_CUSTOM_NODES=1 preprocessor
      
      */
      
      #pragma once
      #include <JuceHeader.h>
      
      #include "src/rubberband/single/RubberBandSingle.cpp"
      #include "src/rubberband/rubberband/RubberBandStretcher.h"
      
      namespace project
      {
      using namespace juce;
      using namespace hise;
      using namespace scriptnode;
      
      // ==========================| The node class with all required callbacks |==========================
      
      template <int NV> struct rubberband: public data::base
      {
        // Metadata Definitions ------------------------------------------------------------------------
        
        SNEX_NODE(rubberband);
        
        struct MetadataClass
        {
          SN_NODE_ID("rubberband");
        };
        
        // set to true if you want this node to have a modulation dragger
        static constexpr bool isModNode() { return false; };
        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;
      
        // declare a unique_ptr to store our shifter
        std::unique_ptr<RubberBand::RubberBandStretcher> rb;
        
        // Scriptnode Callbacks ------------------------------------------------------------------------
        
        void prepare(PrepareSpecs specs)
        {   
          // assign the shifter to our pointer
          rb = std::make_unique<RubberBand::RubberBandStretcher> (specs.sampleRate, specs.numChannels, RubberBand::RubberBandStretcher::Option::OptionProcessRealTime);
          rb->reset(); // not sure if this is necessary, but I don't think it hurts   
        }
        
        void reset(){}    
        void handleHiseEvent(HiseEvent& e){}  
        
        template <typename T> void process(T& data)
        {
          if (!rb) return; // safety check in case the rb object isn't constructed yet
          
          int numSamples = data.getNumSamples();
          auto ptrs = data.getRawDataPointers(); // RB needs pointers
      
          rb->process(ptrs, numSamples, false); // 
      
          int available = rb->available();
      
          if (available >= numSamples)
          {
            // We have enough output, retrieve it directly
            rb->retrieve(ptrs, numSamples);
          }     
        }
        
        template <typename T> void processFrame(T& data){}
        
        int handleModulation(double& value)
        { 
          return 0;   
        }
        
        void setExternalData(const ExternalData& data, int index)
        {
          
        }
        // Parameter Functions -------------------------------------------------------------------------
        
        template <int P> void setParameter(double v)
        {
          if (P == 0)
          {
            if (!rb) return; // Safety check in case the Parameter is triggered before the rb object is constructed
            rb->setPitchScale(v); 
          }
          
        }
        
        void createParameters(ParameterDataList& data)
        {
          {
            // Create a parameter like this
            parameter::data p("FreqRatio", { 0.5, 2.0 });
            // The template parameter (<0>) will be forwarded to setParameter<P>()
            registerCallback<0>(p);
            p.setDefaultValue(1.0);
            data.add(std::move(p));
          }
        }
      };
      }
      
      posted in Blog Entries
      iamlampreyI
      iamlamprey
    • RE: So close to Rubberband-ing

      https://www.youtube.com/watch?v=ekapsMx8vR8

      (laughs in thall)

      Writeup soon 🙂

      posted in General Questions
      iamlampreyI
      iamlamprey
    • RE: So close to Rubberband-ing

      @d-healey The actual DSP in the third-party node is pretty simple, the main stuff was getting all of the includes to work together (and using the right class in RB that doesn't need 2 separate buffers)

      The RB time-stretching stuff might also be possible using this third-party node and just swapping a few things out, I don't want to give out false hope just yet but

      posted in General Questions
      iamlampreyI
      iamlamprey
    • RE: So close to Rubberband-ing

      Progress! I have it compiling and "working" in Ableton, as long as I stick to ASIO and not MME (which means I can't record any clips just yet).

      MME / Windows Audio crashes instantly.

      posted in General Questions
      iamlampreyI
      iamlamprey
    • So close to Rubberband-ing

      Hello old friends.

      I have a real-time pitch-shifter working completely in HISE using Rubberband, but unfortunately it's throwing all kinds of strange errors when it comes time to export the .VST:

      0e5f6c96-61bf-4f36-9d6f-a2ec0a6bd182-image.png

      ChatGPT seems to think it has something to do with the Rubberband library using #include windows.h> before the JUCE stuff, which is causing clashes.

      Christoph also mentioned:

      @Christoph-Hart said in Connecting Rubberband Library for use in HISE:

      Just looked at their repo and it seems that they have a single .cpp file that you can add. Just include that in your node file and you should be good (you might have to adapt some Rubberband compiler flags to enable a single translation unit build).

      If anyone can help me finish this one, I'll post the full writeup for it on the forum 🙂

      posted in General Questions
      iamlampreyI
      iamlamprey
    • RE: RE: Buffer.detectPitch()

      @resonant here you go:

      HiseSnippet 3990.3oc6as0aabbEdojWmXVm1j1ffh9zTl1ZJaYIRJYI6PqXYIQZIDcKhz1A0v0cH2gjSzxc1t6PIQmHfBz9Pdq8w9V+qje.o.AHuWfh9dg6u.2yYl8J0JYIk3f.TQCPs6Lm4b9lys4LyXtsmnMy2W3YjKeygtLibW0rwPGYuk6Q4NFqshQt2xTNvg4cSIyWdKikF5R88YVF4xM9CPRxckKYn97h6sD0l5zlE2jgwiD71r0484x3V2dwOhaaWmZwZx6mf5YWbs1BmkE1hA.bF2rjgKs8tztrMoHYiYZrJ0umQtqa11pUkYrlsckYY2YFVmYu87kqLemxUt07yVgN2ryB8wleNZEibWtlEWJ7ZHo.7MxcokDVCazSruiV.Oh6yaYyvWJaz.jrt45BaKbJhsZrbOts01g5IeCiblaGq0FWq0dWyM3V7n1i0duspCR7HRp.yMVZ3MdJ3U93fWFPJWBHcIMjdGyFs83tx3dP77iLWyQx75PA6TRnno0Xr+y6Ztr.nvQNUe5tr5dvKQin3rkJMIo7bklnZ97SOMwmIkbmtjAtj8nd94AymujX2xtI5wPVfDxptL4xh9tBG3khEBIn.vF8P1bql0Z.z+jBKWXRRgkee76UTeodrF9Uc0Wp2ef5K0i2W8k5wkJ7zp4AfP7XcXdLvUDDChuEHyNaopD.w6CddjVLBk3ays.LRkDeQeFwUvcjD0nkpAsgvhAC7ZsAH6eM0fCdlH7HWq2yuV.12XsMeV8cp8w.wUJMEHFBR6FbGd+A8Ic7X+gA.TFRjBBN.TpSR1mQ5JHqu0iIMe3l0B4z8+jXNURwKjSzCFgSc..zYfiEDV3Ho1DKlj0VxENA7Y06uyFas4ZK+rlqtSsFqt05q.brzTyTMIv7X1TIeOPUz20lKG.yVju8nd8EN71IXZdtiM2gghT0.g5PsG9b11bY6dEQXMQ9OKOLsI7Njh+RrAxm+4J7RtKozDptzDfeBs9SAdOEKHYGHQaGpF.+ADgdLnGhrGXTnNLaROvVFMZOlbfmSU06GlW8GaQaPIzGBy1THQa1b2gbCR4JjqC5NYuorEcUnjL8nNFSPlNljJnScL+ZaK7gLdArTQkm.T5ECEzDUSPsE2Wh49VF8P.5inhbyjbZB.SkKUJPPvbcsNfegfzAb6Dc5LIweeToh9JTqOkh9aDGXbQZWENns7KlRfSP9PxsJMAYDMcxovMVXDL9gjRj6QJS9.xMKWMZHm04QBy.Lad.SCWhC3ZlP6fssliE6.jiESBqeMXmlPYslP8b0QFEl5GFjJ+vShXySiUfKSsaO.7jYDI0CRyDGljfS5tpi9.KbjjCAdIth8KVYRRJzcSvUB8QJG4ajRhVr83TUDA2gr5yQCjCiAqbjgiAz8B5Phal.No73bztZgS6TtWQRZgTomVfTPkSp.XJS3hNhywGjUeq97TNv.Skm.yg7x.aJr5yKjbPbGLRNL7HC2x6RtkJjdfOin3Egwg.aOx9zgDeNPHgKulO4SG.osnjVBgsh8g55UXsFzM3sqq9CrhhuvlMkqGGWJ4lo+THXNMBUnwDPO3m4DG3NBMqDph0DFowyl5GBpKMgnhKaZpG5IpITknLSBaF4OnoL1+Ha5OlI80mNeJGOOc9av7D69ni5KbCk07lEh8pQGWvBny3BoevTvtTbMQvsQho+iRCmYBbky6M.3qTdQx9FIc8h8RhTtSDqAyGmdmnB0mLF2SpHZRkNbxD5GHQvgpJQZ.iyEC1bsAOqVC5.w3jeCYMGzcTxwn050aFr9nkaIPonJyEqLYE8nVRMnFPwmPgNEjoKPwxEUitkNB8EKEQCHfm0XseaMbEnacqYlKxy2uO01FvCjeXnX.wR3bMoVg6OvEZ2VruZlknLf0qs4CZtZDmB5oSGLFslSWvdLUaOFLs.gVDPvzSC8gFjGycrD6iU.VDaYIanD59TmUoddbefvrIaUpiSbmasGyyl5VDp.YBcAUtpUkf5ydptvOrzP.7dL0xzdXZz.cN2mP8Is3cw+3J7UEvl2xE46N.cZsFTSJLwkbleQb4pB5VWm4zU1qvGjPCLI16lC5CET6.dl9PmUxeXP4mPXAfTIHMG8pKfeKGxwfYZ.nDuRvnEvDNflBzIpH25fvJXB7+vlf79gVTb4t.sNX+afULw1AT9nlW6+sZXYSvLyZPaIogKDA3MnO14FCrkbnHKlOX622wWMdKBfce.M9vJ+J0nevPvoAyoGlFMUYdSSn6I3fSC3pC+QfycnhZ+iL85.FWUwYqtcih8oA5bHFcP+Pb5mpjsTcjhLvle6pwoj0oVh.5BjHt+jROsZFjnMo.ggMLkspkjz1y0uQLG0rKv6VmxnisP3UbDVNc5oyDUIY7I0hIqAoP3Ta9yYDPwDOKfRt5A0zyA6aBjqFEVQbQNV8bUBGVMKATCmHD9Mtwnk3lfrmveZhYO7VXMSQHSICTEUoJoG4tKjZdAMcT1GTvK8fvZpTZo9bmSTc0ahIy.+SDW62nS1PAjXJlFGILgA3MDPbHfoW0TDhNZoI6ti3kLQ9QMeinGudBEYJV8zXQc3npWvvWGhGHtL5tXcZ6Ad.VI1LkG0oaxZUwYs0AJsP5VeD7GktIcv.n1Wh6DZDz59hQ6Ib5iIuAlQIJ6xHBJC1Etwvy.6R6+pQo1tF4snkU1dEY4VqLgoMIeXfl43bOhzaoGW0QIRqx4mrcrqGsEXBACdxb0ijLWyrnDy0Nf0dfjg1eObO+PN0fzsvhcHApGo1vxzX0m35WelivquJOgE4ygZPZyaAq7bX3piaP65n1ub8f7sECS7lLWKraNf1vrrfbviZQIL.u3CPIQc6oZ.RFoHBWnsCtKgiI88rAV1vEiSuMbvjk+vI0SEb8Qbt45wbodpRefhg7U0HAUlI7rBWtFVpQCLbkS0LLXLECcmljTIpBG8X03AqFXjb0QqZCYZN1tvJHx7.E7A+3X00xB2gZ0kVZC7whLvF.UGiBqBR6.6o.opOS1SXENv97t8BptR1CrlPvTfRNg77I6yrsmZpoTiBpNAYzN.aCJpK4DMwV+nXdD8wooCuT5K06iNjC0V.IuO.dQqOEb80G9j2.GBTioBPBM174cAEvjJ1Ov0BKVEoXcZKrVbn9ZbR.SZajaAlBU8oMUbejJBUMtkRhX8I4ioD8dUO.ag0tETXXrmanlOxRTMpJKKF3PxkL6gXkqIUsfSSKVv4W3KSIHnDcsnJVoDTDYjlW3f6EaKs.uBnkHi1UmNY1GdRhdBrL5L6VKqSXfEgUUagQQP9+ArHBMxcozGA6kOcGAaa8AalfPgCVewVtrf2O4yP1H3jQgmd3ZqPkT7LbCZKt3XnMbaosY5Sz8Jlqv72UJbMxMVzwoZjyTp58sBNuWkaiAGDcdyvspYbPzwr+GoKNL4Yti9Wvz1DO.NDVQr8pxfS1Vy151BJdXuM4f1.49OwLYSkSHB3SBQzbw84Vxd.Ny8WyYXzigAn3a0iUC54WdyOhMrkf5YYzpa38.bkKaYY8RjQfOX+3Ve4Ke4uHcqUBZVQLNshINDVJM8X+tblpcc.R6wHzfMUTd1oJo1qQvl61psjtGaSkOCzsJqpp+0E6CCCZZl4TuuJW+Z4Jyqde4A9RQ+G3Qc6AkukZnqv5PgcBbeWnTDOr5dnWX8PcmpMpArZGb+tPGklZt6n9b6aWZ96TZ9xJpZJ510lgGOSJNiWwPvNjPnnZ6g9rGAo.DdIvRjzfNqaSkMjCsGgSaWKzFLZ6MvX5XoTIr8ZNVIj8b4Ozv3nWFwaaBfFO0rz2MBdCRAc.QqotPB7RGb74xgIugoyvElT5DuvjSKDeGS0xqYiwwx.iPb7qCLFbMSukYMX8k1xX.dIy5ex48NkJc5uSIyvHzfjAehR3ug9pASIeHlI0Aj3ig5PbuQvMLk6KLiSOGTrQSw1.8E8ipncRRKnb3ca.0fczb5Aq3tDRQw1AGLv4I0+k+N512L1QcDYcgBq83PZPSPGnOGovis.0Aigo6zuWBeG8UZvbrTufosB5rbjBC5rbXmIVvXSlbeg2tJSTvyF4dyj1i6aaK1GSkyCbZA6hpssE1Cc6g6aBaRSQHHueew.0xIJjF6wuTnkHDzqR8aR41X.PiAvFxbr1xoAvG0U+la7GoOVCDMklB9GDPrIjvBT20oX9ngaSwUCdGSbMTvMFqRLxcNSOJ00+F1CxqvD6Z7.Si9pUrFyDrPIV8T6BiuAAnigr+plq4+HrWXqVFOBcGvYghmZ0oFnZj3i2KlwwBZc2YATcOmEfFgfwRg.sd4Un1xBA5ddMppL1FVBCJ7l4EhdPr3vdSsrK+5B0X7YBYiI79VYxMSnvMM82qSZf+Vlc31njlB65nfVOjyjoFi5rYGDVzWcM+ieDwMte1vH+ZPxKKnTR0.ByFjT8m6xQuA5+M3NAyx3Z71PsK7Ds8EeyhMjLWcTc7RWM1ksud5Gz5h+o9+i6o+uMPzsYXzDJspoPwvfSfEEbRJRKrE+6KFT5yHs+WVz3DP9hyLy2buQP9W9Ue06e5Ptw6+e+ZExG27iOVD+PG0la.CZjP93+Mm83+1+7dYhXnBDiSVWW4KOht1nxoDwH6QDeYUYQmEPGUdalfNYzZlfFx1eDP+h6cp.8u+msx2D3fznuP.aKzo6YD4K9ud9eNK0sp8yAxe6LT2vr4DT2X8z+.TceRf9Jl0bnPwIVmcb+hi22NQtvKiI1pbQxvKRFdQxvKRFdQxv85T9hjgWjL7hjgWjL7+ySF9iME9s411v2PtqzIEeOyfKPZpTzbzriivjuMoIG4zFGskjIMQFmNoIv8rNJivyewlQ8ROCupYe75y08j4gXn54ULgFK0DJUJ6bu1hHibQdCSMCds3X6ehurjPraep5vKOWW7UxyD+Gq6Dxu.XU8+lO0QH9dp2IOltGi7.lCyCOQ+xmvObku9z9CWw8T+CWQe8QM8nN9tB+zlEVedS7+1VIaDuOFXMR0k+jr8kETuL65E2qNLIybLMnxAdpStM7DcSb2DieR+dXN1i7N80zjf9Lt0fK855GGykNcWjwq3dW+g+USk5dWtRHFavwjd0fnTaLgIhwepYPzXXqocx2P3HBOq+XOfcXROd2trzIExZBceoj1d23Vd2E2gA41R5M+qVbcvKj5oVl77oKN6+tlxzd8yM0vkfQljuM1suWcyNwqWb7S00K9cNdO+W032MP46iKS66CYzm11S7r156fD8keSUKv71QUpvUL2.emT1XuQWME+Q27r1sSypiLvJm2ANy4cfyddG3sNuCbty6.m+7Nva+pGHtn08GHE80grPIYaWSWNYtvZugnCi+GTk9oS
      

      This is specifically for low guitar tunings (46hz) so it probably won't work well for the Sine Synth

      posted in Scripting
      iamlampreyI
      iamlamprey
    • RE: SlotFX (Hardcoded) Persistence?

      @Christoph-Hart said in SlotFX (Hardcoded) Persistence?:

      @iamlamprey you could add the module to the user preset state so it gets recalled correctly.

      Ah cool, I didn't know about this function, thank you.

      @Chazrox said in SlotFX (Hardcoded) Persistence?:

      @iamlamprey sorry to piggy back your post.

      No problem 🥳

      posted in General Questions
      iamlampreyI
      iamlamprey
    • RE: FX plugin Dev needed

      @griffinboy said in FX plugin Dev needed:

      We just don't know what the steps are

      Most of it is very straightforward, the ugly duckling is Windows Codesigning, lots of shady companies that make it unnecessarily difficult and annoying

      posted in General Questions
      iamlampreyI
      iamlamprey
    • RE: So close to Rubberband-ing

      Yeah the Rubberband license is pretty...

      ...flexible. 😎

      posted in General Questions
      iamlampreyI
      iamlamprey
    • [Tutorial] How to Reset Parametric EQ Nodes

      Since there's no API call to do it directly, you can create a blank Parametric EQ and export it as a Base64 String:

      First grab a clean EQ's state:

      const cleanState = myEQ.exportState();
      Console.print(cleanState); // copy the base64 from the console
      
      // then store it as a raw string:
      // (this can be an external .js file to keep everything neat)
      const cleanStateString = "180.3ocMNEsCBBCD6lDLw+F3WPDR7AMpy36S1EYIic31Pkud8.g6o1z1q8jmpwPf7fH85PGBhMoE89WX4SX+NPrNscn7LrcnSEBnFDhji8sUFaD8ANi.3qp5ZoSc29WWjTpMQxKipHxdR1R5AYC81w3zb3lIXXu+IRxRinL.JZLV8ok8D3+dg5iF2iCpn27gKiaVR89ZrnQ4bncZ.qfwnS7rkAIQmdh7kuYw7QtXVLeQD9AnwXTa";
      
      // Now use that to restore any Parametric EQ Module (or any effect really):
      // ideally in a button callback or something:
      
      inline function onButton1Control(component, value)
      {
      	if (!value)
      		return;
      
      	myBadEQ.restoreState(cleanStateString); // easy reset for any module
      };
      

      Here's a full snippet demonstrating it:

      HiseSnippet 2866.3oc0Yt7iibUEGt7LimjtCIYBj.QnnPQmHQO3Id55c4DkD+1se+1ss2Lodbsc0td3td3WihRqrHPDJHVwNjhPBIV.BV.QrAkAEjhXCKPhEr.Q9O.DHjX2vspxks691YXRKHRLK5Yt+tm647cO0sN04NcMSCIfkkgIVncasXB.KzWHbyE51iRMRPQGKeZrPOS3IBlBZ.aSEoWBbxKYBr.1XIWLQvxBHiEJzUy4ZZnctFl2e9audRAUAcIvFILrNFJRfRJZJ1aTqEunhpZVAYPKEssrlNddIC8TFpFNPrtZ3CvlHHMVXHnhfqYWIL1gBVivB8MCKIKRRISKQRChQAFPyyQPxMffjgilTfklFNGfiUfDKz0yHqXaX1zVvFXAcZRC4EMGYLS2O.cTrTDUAtCHvZBiruLVpQJpx0BxQVXXgtVsMYrq5mwd5vkUjUVquIycCuIv2rhsSZgtxCBIhOCHEZKjtlOROU3lRlJSr2LiKOOV3751.yABvmMaihusXW4IdzvoLfVnaGUSXLHqIbv5UrO6AGbKb3Ot4qrK7OvmPV13SELw0VjoN9qh6cpI5PfclAC.R16umq9dPi21xjBxeZF6Mkq86d6ai6XAvsGAb+oANzL2A3VtYp8st4JOJoBDz8xdPG5Frnf4SLLs8j1ecfkULsWrwLuvbdKcC4BCGbIAcbIiIKvg4FavlfhOvzPyanqOMTA6lx+uiNwTQ2d+Mn3mb7bHzbcbKgo.bEabAKbAbSgYP+AWwvWdWjMQSuIfLtGA+AQoLjJWIiUpjISklUMcoYQxRcTszM3RTdxBJ1lDY5kWQhhn1XGY9nCYMHVRbB+wZSlUa.2fC4YpkK4nxFY3icTW5lciTolYEKIctRlR5MyjTOa5QGqva0NqPZ9DUThRcxDl9FMkHiczQG2ZR45yKpL4v4xMHZvjnWJdhYTKEoTy2sqSj7Ml2PQuTzB8K0g2XLeZpHxCYTxRpjZhNI2vhJBcZvGquodcZQco9QOYvL8lbliSjutl7HtwN8lwU2tamRf5oikPeV2VB68J6h77ZSFgMFuaFoWaRt9sRkNa1nfQmvlndshwRz43FVcKpmqvxx.MwZry6zLQh1SrGK2VQRgHpPy9Mm2msQ0kik06WeBUdm7GlLobs5U04UW1dRsYEkqMME7cpH4z0OjxpeCYxdErqxZTpJsTtXylJnRleTRMhhUpTZTd5p8VPy0sQNcptkGYqnsfyrn7Py1KHLGITV6j4SVJVgjpqS+R4MRdjwLSE1ipNqXA07sawzsT1SFITypgwXmHK05ch5R.sYT5JIrXXEaOOUKp1S6FQboYzCrSsjnntFWjQxZCjjO3PPuElQUflVTTRiqPIV4plLUlZyVtsZSKQctZsRR1mlrxxiakbP6rKKjRbLsbOM.ubeMsoIHa3PZn2O5r4Q60ap4HJJ95EkZTXnfMeCxpCZDcQgA8x.mYJ2fTfQhKJHtfszbdUkIIJcRT01QWLQbgUgkQLlS1qsMsjHgXlIvhEQKcRR01zpBkolpMjn2LZtgfoSZUdPu1SMKzsSykBh5YXUZjV0YI.TZbT0ClVARg5Tt4BQ34j6Ut9BNMsXicWT4olY614nkBlyIXUcVvLWB9iA8jlZVIFcNRtnBlD00SaSOuJfbrQZnd2N4JIbXImY80TaPo1CvxWcLbOkcPuTSMGpC5zlWGlh3hxSYlABPYu4ZZSmPdQ+HEhHRzBFPpbwNrcptDoKIjTsLbKEYZUoZvjTrrNrGTUA5QU3plZNdpXjhjEGeXEMRFlSZlePuhSMyzkXjPr7Z7mjSqQZPpr7BLYyWjinm2b1KkAFboXJHNieAghbwIYmZVtKQ1RBEYJKO6vHCmTuqbrL1EcgHaNCNfZN.WdojhN8j5yn2JgV+X4GVucSwlbMzp0QuQWK01Z4EGNXoVwBKTmWknpUm90OXJyvdUaOZVVgrQXNPcP0ZUpHo4zoqR5HCD.8HO9vHYZl6.0JjV86pnavCsjbPTGFZE.YthrNpN1sHXJ0nM+7b4NpUexk.IYCQh9FUbZoFoAQjnGm2pRgp64WI8XG3qwCLLwgeWy1uXWPcWXuCvO4A7K7d92zWUHVzw11PGW0Xnhja4eu9M7J9loNzUJ5pJ5vJxN5R1JP6LzS5s.B2OcYZntujg1DCc3GwtE7iNpNfat6c2cGkA36+0WMbmcLA1Nl5vvsyEB14KJeyWAGxEbC4sy79hvtuIb0AerD9ApTAAc+8Vgyd2LpkqtGToDTUEgMwr+4oEtm2ZmTwvFTUeeOh28M2E+7SMXvEN2JeoBLuvoc6tx7Asv80czDAlamubMD1MwYaQ45ObsnH4mV1xPC875J1Um.VMNqgpraqGt+azFZvVkWcasYEgPSs8Zr4IV0XieVDSA5jcBuJkh4g+1cch0NeZAag.GA8ILNS.l1JtamPoASgck52wzNgSCrFaaLAKT30OLgsLYupEO+vlU0Pv8HcKE311M3OY3skHvluI7oiuXyfJwWAfez9xgSaJLbn.L6kUQE1gUMAcfJl3vftd245AaAEaf1F06e+6e5YUIWYLkWLc2sgNL7c2EGeu04z7x68x398icK2IxqKClCkdIBugvmFpFyNxvbrErQa.bhABpVvi3XyTjsgMZGNzuJDF1HfxvQ1tideLuGRWxjD4mVRJtQ7UAbkvo2MdPPCrW9yy7XwOk7neqpe1Rkamut9CwY4M4n3K29fzqE2FLG5iGIbCu6goXU1PC5UASnQgtx1g4Z9g4wWElRBhvTiaTdjvphpUL1JFmV+rw3gxM656FY36Ra4oNa4oSGGGC8xIvqEYH6nJXe16J4dKxUS.qNclKn3dIDcKE6EaeKyOCWf5fG3EndXQ7oBWSwVZzEy3Ut.Fg0c9eAiqt14iG1+xSa.7Zgy18xdGyGb3Cckshe3fJlobLmBxbhWrudX25KmI5Ubz7eozxsnga5Ha1VYzceY0mtq9eoa.i0vvws5RYA3mqgGECCibS3qzR.XtQGVPvCfq3Vzxe7AA.0DnK6M.VO39qljvcbnUSRDL41O.TtnDvNgWUX3AjCdZnWSJ3ER+yHui8yG2UfXkvoO2qlzSf7r++onKScdA5MB2vaILqD9je1s+ienq.6Jg6c06jwyBty6C9yKD67NkH.03FDunuR.qw+kuXKeEDXIPnk.AWh.dwds26F2ySI.3SYd1N91fPLABxDHLStN898u6W0WIf46c2uituBByjHLShvL4Zlega8U7XlLf4O41e6I91fvLIByjHLSEv7oW84+ZddlJf4OIyG9191fdn.gYJDloBX9zW3VeCeOuNO+je22w2FDloPXlBgY5.leimX9y4qrl4R+qefuBByznmjQXldMyu0G+W7NKSu9v7eP5m5aCByzHLSivLy5yFu8GeeOOyPbdOyfvLCByLnu9EvL9G7teQek.li+iu+a4qfvLCByLHLytl4O3c2yWIfYr+7u4G5qfvLKByrHLyt97LE0WxWIfY7aj4i7UPXlEgYVDl4BXF+W7t+cu7L25yFy+s+NeaPXlCgYNDl4BX9deue+i4qDv7a7q+4+HeEDl4PXlCgY90L+7u0y5qrNO+Q2487UPXlGgYdDl4CX982+O8L9JqeGz7e3WQhGs5LBy7HLG6fMuo7W8Kgut9brWefuMHLGCg4XHLGa8Yiexs+m91rl4Sl8s7sAg4XneR4LL++yc.74QLzDjLMtij+sdca75Q8Tf6acue4G6Dtr6X7K3xtZJxJ2QR5rtBYgjW1ERcYWH8kcgLW1ExdYWH2kcg7+mWn6uJnDN1FZ9Wc.CqbsL9WyLzVuQf8uA9FDSAB
      
      posted in Blog Entries
      iamlampreyI
      iamlamprey

    Latest posts made by iamlamprey

    • RE: FileSystem.browse() if user cancels

      @d-healey Nice, in the meantime I'll modify it locally.

      @Christoph-Hart merge request initiated 🤖

      posted in Scripting
      iamlampreyI
      iamlamprey
    • FileSystem.browse() if user cancels

      Is there a way to check for this? I have a hacky processBlock which starts running when the user clicks the button, but if they cancel/close the fileBrowser it causes a runaway memory leak

      Something like this won't work:

      FileSystem.browse("", true, "*wav", function(fileToSave)
      {
          if (!isDefined(fileToSave) { /* do stuff */ } // doesn't get called when cancelled
      });
      
      posted in Scripting
      iamlampreyI
      iamlamprey
    • RE: Third Party Node + Offline processing?

      @ustk Turns out you're a genius and have already solved my problem 🥳:

      https://forum.hise.audio/post/91573

      Can replace the Sound Generator with a ScriptFX and use a midi container to get the gate information, tested and working in the compiled plugin.

      Thank you very much ustk circa 2024 😂

      posted in Scripting
      iamlampreyI
      iamlamprey
    • RE: Third Party Node + Offline processing?

      @ustk Yeh that would work, but I also have a regular old HISE Parametric EQ as part of the "chain" so I either need something like:

      dspModuleA.processOffline(buffer);
      dspModuleB.processOffline(buffer);
      // then save
      

      Or I'd have to recreate the parametric EQ in C++ (and a custom panel) which, yuck.

      Update: I've gotten a bit further with the scriptnode method, I can compile the network if I remove the analyzer node, since my audio file has a fixed length I can set the record/play manually. The file player node is very temperamental and starts playback any time I compile for some reason. I'll keep noodling.

      posted in Scripting
      iamlampreyI
      iamlamprey
    • RE: Third Party Node + Offline processing?

      Bumping/updating this, I can't use Engine.renderAudio() because it's an FX plugin, and the scriptnode setup out of this thread won't compile, and crashes if I replace the Analyzer node with a manual gate. I also can't do it in the third party node, because I also have a Parametric EQ that I'd like to include in the "chain".

      I need this:

      1. Embedded audio file (already created, only 1024 samples)
      2. Pass audio file through 2 modules (third party node & Parametric EQ)
      3. Render output to .wav

      Edit: deleted my old topic on this because apparently i have amnesia

      Edit #2: Enable Sound Generators FX also doesn't work, since it's still muting the sound generators

      posted in Scripting
      iamlampreyI
      iamlamprey
    • RE: Third Party Node + Offline processing?

      @ustk You can generate the buffer inside the node without needing to pass anything in, the global cable would be used to pass the export filepath since C++ nodes don't support strings or file objects.

      Best case scenario for me would still be using DspNetwork.processBlock() but I'm not sure if it works in a hardcoded FX module, I think it's a pretty old method.

      posted in Scripting
      iamlampreyI
      iamlamprey
    • RE: Third Party Node + Offline processing?

      @griffinboy Yeh I just mean running an audio file (dirac delta impulse) through a single hardcoded FX module and saving the output as a .wav (while ignoring any other incoming audio signal)

      I've been doing it with a AudioLoopPlayer, MIDI Sequence and Engine.renderAudio() but it's a bit clunky. I could also do it inside the C++ node but that involves global cable shenanigans and potential race conditions.

      posted in Scripting
      iamlampreyI
      iamlamprey
    • Third Party Node + Offline processing?

      Do third party nodes when loaded into hardcoded effect slots allow offline processing via the regular dsp network calls?

      If not, what's the easiest way to process an IR through a third-party node? Something like:

      const myCoolThirdPartyNode = Synth.getEffect("myCoolThirdPartyNode"); 
      
      // or 
      
      const myCoolThirdPartyNode = Synth.getSlotFX("myCoolThirdPartyNode");
      
      inline function offlineProcess()
      {
      	local impulseSize = 1024;
      	myCoolThirdPartyNode.prepareToPlay(44100.0, impulseSize);
      	
      	local buffer = Buffer.create(impulseSize);
      	buffer[0] = 1.0; // Dirac delta
      	local channels = [buffer, buffer];
      
      	// process through third party node somehow here
      
      	// then save the modified buffers as a stereo IR file or do other processing after
      }
      
      posted in Scripting
      iamlampreyI
      iamlamprey
    • RE: Question about compiling nodes

      @Christoph-Hart said in Question about compiling nodes:

      as the autogenerated example node does.

      Okay sweet that was my starting point so that explains it, muchas gracias

      posted in General Questions
      iamlampreyI
      iamlamprey
    • RE: Question about compiling nodes

      @DanH Yeh I've been doing that up until now, it's basically muscle memory at this point. The network wouldn't compile until I enabled polyphony

      posted in General Questions
      iamlampreyI
      iamlamprey