HISE Logo Forum
    • Categories
    • Register
    • Login
    1. HISE
    2. HISEnberg
    • Profile
    • Following 0
    • Followers 5
    • Topics 63
    • Posts 688
    • Groups 1

    HISEnberg

    @HISEnberg

    321
    Reputation
    216
    Profile views
    688
    Posts
    5
    Followers
    0
    Following
    Joined
    Last Online
    Location Canada

    HISEnberg Unfollow Follow
    Beta Testers

    Best posts made by HISEnberg

    • Oscilloscope Script

      I am trying to optimize my oscilloscope script while still keeping it visually interesting. I recall having a discussion a few months ago about this but I can't find the original post.

      Anyways here is an oscilloscope script for drawing the analyzer to a path in a panel. There are a lot of adjustable variables for optimizing it (buffer size, frame rate, frame count, etc.) There is also optional opacity settings (glow effect) and ghost trails (though these consume a lot more CPU). Hope you guys like it and let me know if you can think of any optimizations or better visual performance for it!

      Here is an A/B comparison of it with the floating tile display

      osc.gif

      HiseSnippet 3084.3oc2as0baabEFTRH0Vw4Vmz59HFMsy.YISSRcupNQzTjRbh3kRRY0LdznYIvJx0BDfE.zxJdzL8WRm9SnO1eF8w9X+Ij+AsmcWbYW.PKZEeIN7AEuWNm8rmy24xtKRaWGCrmmiqRtE6c0XrRt6o18Ja+gUFhH1J02WI2mp53Y7PSb+ICTdxUiQddXSkb4l+.5Dxc2ETX+9wu8IHKjsANtKEkm5PLvGQFQ7i6s8deGwxpFxD2iLRX1quWcCG6JNVNS.gYd0BJiQFWfFfahnSaNUkCQdCUx8.09EMJfWquAB0GsCd6s2n+5au0NHbQyhaswl6rQgc5u4FasogRtOopIw2wsqOxG6ojagm3XdU2gNWZyWfmR7H8svzFEU5BqLu6ZNVlzsHsWkJCIVlsC0RdJ.SaGqylmqy9Z0FDSRT+w5tujMfVLEhJvbyIKdyKIdEEEuBBhWFhTNAQZAtH8UpcMbIi8iGgaKqa6icOGA1IQQgOWk4962WshCLCa+7iPWfq4BMhnPe8BEVUC9yx6tnMXT7.CDVqkmAXOAHhyX7huZQM3GXG870dAxUqrMx5JOraQsGqwfU4Gf8qd94XCe8khFbIfgxz8jiqUqZGQh1m3M1Bc0Sl.D61EvHf7bCb3rVcq.rfyqT7PuPJZFaacFrc.ZBUB.QUbFM1wFZnuTv3YrZ9.T1Enqp8.hMNugKFLoT7saq9OmtYSKeS78crKN00JX7vkRfZ5N6npMOn2g.wqWXmM2US6QORqC1bhA1TqOa2o4Q9Ar14NtZ8w9f8SqR6iiYjKd.iMs6zpc0N8pWsKvpWsDWwbD1df+vk9iBKzpZK0bxH.Vaais7fgJdMWrBzx48v9cH1C3L.fbiwt9Drmt7hrbJhJa3SdAV22cBN8FsW0FsOKBIv4cfpUOV1VdWIh1uS4SlIhRtZ05TtQ0ypz53l8.BWa2HE04t.VmyHOXjmcZ7PFSbcAqUM5LpaaheILdAg8AXVpfLFBVkwH+gZNLrfWD4ifvDsoCDCB3RJsyPHCch9tHh0MNS1enlbcBSNzHZ+IwsEzwJqrLaVb+T17E1c4GOwan9T0YAKy0x3nQnA1D+IlX5ZleCQbdixGzrdui2u5YcazpECuVH+NRyndyyhlEa3BaHq+1GaPFg7IN1hF4pUp2nbupmUqbkdsnF50JsaJTP0mVsy2eVyyXp.JBuXBiZEmIzXaILZAdYkq7cGzATa6CJuiZcLcQJ7xBA+D2CmT9oUq0pSCw4UtPsZkqNk408vx625D171Dl21Sie85Tt9QroslD6BUMG4bIH7u.YMA6I5pCNePqQzjwBr8fiZApDvunBEeS83jLDs5UuUS9R1MiwqzpSypchsiqkWRlaWt2gm06v5U9tlU6RouT9DBaiHXxjwl.vh0Ow1BBWpc9DaCpENXnnopmDrZ4XfrzrwWBSAVDAecZzSA51MhDQ3Y7+9AowlqDx2GnoWLeAsGlZJSgqM.WPHa4K0k.yqFOmz9Mf9fEyPyCR+Ck3.Klis0Ufxb7UZlbDOMngCwNHbQRMEkPbEgvOozUICDD6HSauxiS5DsbDkICK7rTg4N8YjSk0+PGwZmn+QVwG0S24JZEWV6OHEpJCM19tnKgbLQ5fL0KlvjDqHQevpPrWHqEydjTGMH+4vLKaYomxce4L1Nb32kDSVfXFao3tSncHh43yaHlLXnu3DOj0S5YNhX98v7BH3QZkl5Z6A+AadXHmCn3AAXPhME4x2oIWCphgtHr05gRL5QkRN49PfQSVxNfaLJWkusWUhvrL4AFJdJKNLxSibtF1FA0xZFMOnKcofNw3uWIiDo3XFepaxyupE0JQ5sv9CyxkMCCW8Xd93z.0kSQRPvNehcXjQweo5fqHo91YB4eX7lXEwMQl9Ao4qCT5Mw+JVFys.loSWnGIynG.is4LHoQEWj2vBibEgmI.enQisjh38PFbSF7IE4HbSd5yJb5xYv3301yG452DeY2I8Y0zPgwAPU1xt7LrShB4kHz1sI.X13lXkAKTFAz4BEHApblWxtYR2OI0GIS0mrJjFIrmi9KuYM20uInBWZvfJNt1fDoKUGvrXSFPKymepdc9+wK+kD+gksFODoKWqypQ3ZJ1cir1wCxSCGwPHQR3pAwqVENBSugDiKrgC7BGRIQQILddcBdFqItd5wxn0oypfWH+Oux8ztLbzgnCOXwugroSOb+LAYDYXB+rH4bFbuV7ssajr6yapaysXum1IIZ+ey9FYX+indlbADgMGX4boF+dNjx3ITD9zzSynCC+PDqlQl+j5.AWlvMzL6w.kwe8qWGIJtINIj.g2ZY3QktNc4yA2CCckaCbyuiyDHsLVOrJP8AKq8pXQLqZB8GR7jJQmO8qWV9TKrqvQqVX0kAmMlUmNTJ5xYVBpiMipJHKq9HiKRUXd30ePYRGLxL35nDtsir.U+zJmWtRcfKBqV1ktm5nXwyIiydjsWP2KHiCKAz2QiLZrqyKvoNeZz4MBNO9Jqr6hoJTK7r5eyiy5v8SyYJDo3hGSAJIqsI6aAHFscs.ZfcEeTHmr0Mg0VD+jFXDbodzqvw0wR2H759VkeJ9jHE5lmM.sD0hu1Bjuwa7Y50zvn+4b5edBj0ymo5jRcfQxoO64mxuNmoktMUwBA5WZdJlFUesBYF4Aa4gyjPmwb5BiWHXIBz67PULcuf8S1n.TesfEqoiOtkM3C+pEu6hfPjbnyOOywBXlEUbxX3.Ac5DpaOYTeraDtHXhJ4VP9ICTm9SFH9hFF76LTXhN10AG6Viw1S6cNTBtnQ5yKDHUvT8YOtvmG73BbUmBwTI2cUCziJLQV7ggTNt99HeTHi.dFeCwPe6ieAw.ye0h6ptO16BvTBKUzkgqj6S3K6mE9lFzCTyV0EUCuSdkWJ9vSWI1fUbQbGWsG+Xyw8P1SQXwtmeva5vWrZVNHH6xfdDPuRWyuPUrqouvj8lgENPgv28egZ4IlDmvW0Po+fvmC6dpqWZ6har9FaTZMEhOdjz.aUbss1oz1BCTJXjRat45EJrthO9k9hOsFydj6HUZzfkhr50Mg7uBOpxpzQYknRuxeVqZ.n14xSbbuf85OP+minNjWqHpB+Uy.LYVsW+s+bJ012rmI9bzDK+mxgZL.b5m.6KUa3XNwB4K+hbz2sLX.vmS5YvnO0ksGb.DQ36asmoaVEwuRsMw2XX1x3bYHifm06BYL3wM+LUdgrwB3Bp09KuidIy4DV+6D8fzDZs5GDt72SsAxCxXy5X5uHshx9XKjjZ5DYGxueuLdy551u.BM01wB4lTIO+LuKK952kKLyHA5VR6mBZcgYyT75ey6YE49qUYp7OlDXFj3iGA9KUgje+LPb4G4Z.b5SWBDIWs4jQ7GkO7Qhg9xMGsJBd6Bz1TApK11j03+A+BFrHsctfAKFNnnJQkqRVTMJ2H+qYHJWkjdH3iDf7CBN1+y8Z6BEZfuLnPiftuydu07peeqS9b9N4Sgvi1X12IASo7aYs0NAAmz5.rM1kBfJNs3j68u9r+yr9k6Ldl+xcZY3CKeOWjs2XGOIF2EOhzCJSvSryi8v0bw+0NTHsX+UbPtYNzO9s0fMYlzzE4Owk4aTdD8jcutH3KLa15a3CBZg2UePPumir7AqvHoueo6FJi7r9UgzwVvoEhxuvJ7KrWYbcCGamwCcrIFhF8NXvebv.rqnrm4FpruObjvnd9ww60AagQB.3l+i8NB.dHWPOguk5hhuweKWYZu9cpbwkcGUe7j.60Vb67yTwsu0k2O.E599NYw8CSVPyKDmr32vZSuMtojqXN4Zp+2uyyUTLyjER8lfhRYRQI4CB3OwVl07tjlEUUTCTEEEKOHryRhcB0fIwLnsDmZPDNc6+8aqxd66tXXmaxuHZpdVzkr8D3PzrRQKJRXb2kD69PjqIXCMdKjZSNluv7+n3Ce8Wz41J8tL2lxWmJ2lxu+Wf419vdYMy+K5KqY189f3g+71JdeUHtbIMloT6Cis78QAAuOViQHCWmyL3uX.0o+Nrdf8sM6+QNtqZCZasTOTfhxHHkwYFFxrJEgktsDt1skv0usDtwskvMusDt0skvsuYBoYzKOw2g+4XSKyocU9CIjiWgCyMQ4+iYkPuv
      
      Content.makeFrontInterface(400, 400);
      namespace Oscilloscope
      {
          const var Analyser1 = Synth.getEffect("Analyser1");
          const var BUFFER = Synth.getDisplayBufferSource("Analyser1");
          const var BUF_OSC = BUFFER.getDisplayBuffer(0);
          const var pnl_Osc = Content.getComponent("pnl_Osc1");
          const var timer = Engine.createTimerObject();
          const var Button1 = Content.getComponent("Button1");
          
          const BUF_LENGTH = 4096;  // Reduced buffer size for better CPU
          
          reg BUF_PROPERTIES = {"BufferLength": BUF_LENGTH, "NumChannels": 1};
          BUF_OSC.setRingBufferProperties(BUF_PROPERTIES);
          BUF_OSC.setActive(true);
          
          const TEMP_BUFFER = Buffer.create(BUF_LENGTH);  
          const DRAW_BUFFER = Buffer.create(BUF_LENGTH); 
          
          const FRAME_COUNT = 3;
          reg frameBuffers = [];
          reg currentFrameIndex = 0;
          
          // Cached path objects
          reg mainPath = Content.createPath();
          reg trailPath = Content.createPath();
          
          for (i = 0; i < FRAME_COUNT; i++)
          {
              frameBuffers.push(Buffer.create(BUF_LENGTH));
          }
          
          reg magnitude = 0.5;
          const MAGNITUDE_SMOOTH = 0.9;
          const MIN_MAGNITUDE = 0.05;
          
          // Decimation
          const DECIMATE_FACTOR = 32; 
          const DRAW_EVERY_N_FRAMES = 1;
          reg frameCounter = 0;
          
          const BACKGROUND_COLOUR = 0x00000000;
          const WAVEFORM_COLOUR = 0xA0FFAE00;
          const WAVEFORM_SHADOW = 0x60FF8000;
          const WAVEFORM_TRAIL = 0x30FFAE00;
          
          // Lower values for better performance
          const GLOW_EFFECT = true;
          const MOTION_TRAILS = true;
          const CORNER_SMOOTH = 3.0;
          const PATH_THICKNESS = 2.0;
          
          // Magnitude update
          inline function updateMagnitude()
          {
              local newMag = DRAW_BUFFER.getMagnitude();
              magnitude = magnitude * MAGNITUDE_SMOOTH + newMag * (1.0 - MAGNITUDE_SMOOTH);
              magnitude = Math.max(MIN_MAGNITUDE, magnitude);
          }
          
          // Frame storage - only copy decimated points
          inline function storeCurrentFrame()
          {
              for (i = 0; i < BUF_LENGTH; i += DECIMATE_FACTOR)
                  frameBuffers[currentFrameIndex][i] = DRAW_BUFFER[i];
              
              currentFrameIndex = (currentFrameIndex + 1) % FRAME_COUNT;
          }
          
          // Drawing function
          inline function drawOscilloscope(g, panel, mag)
          {
              g.fillAll(BACKGROUND_COLOUR);
              
              local width = panel.getWidth();
              local height = panel.getHeight();
              local midY = height / 2;
              
              local scaledHeight = height * Math.min(1.0, mag);
              local drawY = midY - scaledHeight/2;
              local bounds = [0, drawY, width, scaledHeight];
              
              // Draw trail frames if enabled
              if (MOTION_TRAILS)
              {
                  for (frameIdx = 0; frameIdx < FRAME_COUNT; frameIdx++)
                  {
                      if (frameIdx == currentFrameIndex)
                          continue;
                      
                      local age = (currentFrameIndex - frameIdx + FRAME_COUNT) % FRAME_COUNT;
                      local opacity = 0.7 - (age / FRAME_COUNT) * 0.6;
                      
                      trailPath.clear();
                      local sample = Math.max(-1.0, Math.min(1.0, frameBuffers[frameIdx][0]));
                      trailPath.startNewSubPath(0, midY - sample);
                      
                      for (i = DECIMATE_FACTOR; i < BUF_LENGTH; i += DECIMATE_FACTOR)
                      {
                          local x = (i / BUF_LENGTH) * width;
                          sample = Math.max(-1.0, Math.min(1.0, frameBuffers[frameIdx][i]));
                          trailPath.lineTo(x, midY - sample);
                      }
                      
                      trailPath.roundCorners(CORNER_SMOOTH);
                      
                      g.setColour(Colours.withAlpha(WAVEFORM_TRAIL, opacity * 0.5));
                      g.drawPath(trailPath, bounds, {"Thickness": PATH_THICKNESS * 0.5});
                  }
              }
              
              // Draw main path
              mainPath.clear();
              local currentFrame = frameBuffers[currentFrameIndex];
              
              local sample = Math.max(-1.0, Math.min(1.0, currentFrame[0]));
              mainPath.startNewSubPath(0, midY - sample);
      
              for (i = DECIMATE_FACTOR; i < BUF_LENGTH; i += DECIMATE_FACTOR)
              {
                  local x = (i / BUF_LENGTH) * width;
                  sample = Math.max(-1.0, Math.min(1.0, currentFrame[i]));
                  mainPath.lineTo(x, midY - sample);
              }
              
              mainPath.roundCorners(CORNER_SMOOTH);
              
              // Glow Effect
              if (GLOW_EFFECT)
              {
                  g.setColour(Colours.withAlpha(WAVEFORM_SHADOW, Math.min(1.0, mag)));
                  g.drawPath(mainPath, bounds, {"Thickness": PATH_THICKNESS * 2.0});
              }
              
              g.setColour(WAVEFORM_COLOUR);
              g.drawPath(mainPath, bounds, {"Thickness": PATH_THICKNESS/2});
          }
          
          pnl_Osc.setPaintRoutine(function(g) { 
              drawOscilloscope(g, this, magnitude); 
          });
          
          // Timer Function (Buffer copying)
          inline function onTimerCallback()
          {
              BUF_OSC.copyReadBuffer(TEMP_BUFFER);
              
              for (i = 0; i < BUF_LENGTH; i += DECIMATE_FACTOR)
                  DRAW_BUFFER[i] = TEMP_BUFFER[i];
              
              updateMagnitude();
              storeCurrentFrame();
              
              // Skip frames to improve performance
              frameCounter++;
              if (frameCounter >= DRAW_EVERY_N_FRAMES)
              {
                  pnl_Osc.repaint();
                  frameCounter = 0;
              }
          };
          
          timer.setTimerCallback(onTimerCallback);
          
          inline function onButton1Control(component, value)
          {
              if (value == 1)
              {
                  for (i = 0; i < FRAME_COUNT; i++)
                  {
                      for (j = 0; j < BUF_LENGTH; j += DECIMATE_FACTOR)
                          frameBuffers[i][j] = 0.0;
                  }
                  
                  timer.startTimer(30);
              }
              else
                  timer.stopTimer();
          };
          
          Button1.setControlCallback(onButton1Control);
      }
      
      posted in Scripting
      HISEnbergH
      HISEnberg
    • Free Reverse Delay built in RNBO

      I know some users were asking about a reverse delay so I wanted to share one I implemented in RNBO quite a while back.

      Link:
      RNBO Reverse Delay

      Unfortunatley I am away from my Mac at the moment, but I will update this link when I can. If you can't wait there is also build instructions for Mac.

      This project contains three folders:

      1. RnboExport: This is the .cpp .h files created by RNBO when exporting the patch. You will want to drag these into: YourProject/DspNetworks/ThirdPart/src
      2. ReverseDelay: This is a HISE project. If you are on Windows, after downloading you can go straight ahead and open this project. You will see the reverse delay embedded in a scriptnode network.
      3. RevDel3.maxpat: This is the Rnbo project itself which you can open inside of MSP/RNBO.

      Once you open the project and the Scriptnode there is a little write up about the reverse delay (just make sure to click on the comment box next to the xfader to see it).
      dc1d3ea7-7801-4355-8210-3761829f2582-image.png

      Most of the guts of the patch was taken from this generous homie, so shout out to Taylor Brooks for sharing this on Youtube:
      https://www.youtube.com/watch?v=hOX5eg7QCqM

      I recommend watching it and following along if you want to know how the patch works. My patch essentially operates the same but I added processing to make the left and right channels function independently, added a feedback path, and I also tweaked it to create different windowing functions (triangle, Blackmann, Hann, I can't remember the others). You can also tempo sync it in HISE using the tempo sync node, it's easier then trying to do it in RNBO and communicating that to HISE, then to the DAW.

      If you are on Mac, you can build it your own. I am not sure if you need a Max MSP license, but I know you DONT need a RNBO license. You will have to install Max MSP though. A RNBO license will allow you to make changes and save the patch, but I am pretty confident you can open and export patches without a license.

      All you will need to do is open the RevDel3.maxpat, then follow the steps to export it to a HISE project (I recommend this video):

      Hopefully everything is in working order, its been quite some time that I have looked at this project. Let me know if anyone has questions or recommendations, and happy holidays!

      Hisenberg

      @DabDab @udalilprofile @treynterrio

      posted in Blog Entries
      HISEnbergH
      HISEnberg
    • RE: Reverse Delay Faust

      @DabDab Cool I didn't know there would be so much interest. I will upload it on the forum in a couple of days!

      posted in ScriptNode
      HISEnbergH
      HISEnberg
    • RE: HISE Meet Up

      @ustk Only one shirt will do, hopefully @Lindon has some spares.....

      0d94aa62-654e-4c49-9983-0beb3117ff3e-image.png

      posted in General Questions
      HISEnbergH
      HISEnberg
    • RE: ChatGPT HISE Copilot?

      @hisefilo

      Ah I have been unmasked.

      So I have been working on this since the beginning of the year. I simply fed it a lot of information from the HISE docs and this forum, forgot I had it published. If anyone has a problem with it being there I can take it down fyi. Just send me a message.

      I meant to share it here once its performance is optimized, so the fact that I haven't tells you all you need to know. I also figured theres a bunch of users here who probably have their own versions (come on guys, fess up) so it didn't seem worthwhile publishing in the forum.

      My opinion is that it doesn't understand HISE scripting even until this day. It confuses HISE for Javascript constantly. Even after months of training it doesn't grasp simple concepts like the inline function and ControlCallback. (I don't know how many times I have tried to get the GPT to understand this but it constantly gaslights me on it).

      What I find it good for is mainly debugging or writing out redundant tasks. It seems to be okay with LAF as well but that is about the extent of its capabilities.

      As @d-healey said, beginners should definitely stay away from asking it to produce code. It will very easily provide wrong answers and tell you why it is right, so it can lead you in the wrong direction.

      However, if there is code from the forum or docs that you find, something about the ScriptingAPI you can't understand, the GPT is actually quite helpful at explaining it. This is where it performs most optimally, and it is also helpful for checking your code against (but always take its advice with a handful of salt).

      In short it is good for speeding up workflow and checking for errors, but anything more than that is asking too much generally. Its also advisable to know something of the topic you are talking about in order to prompt it with the right type of inquiries.

      In any case hope you all can make use of it and if there are any issues people see vis a vis intellectual property, reach out to me and we can talk about it

      posted in General Questions
      HISEnbergH
      HISEnberg
    • RE: Cost Of Operations?: License Fees?

      @HISEnberg said in Cost Of Operations?: License Fees?:

      You'll also need an Apple developer account which is about £99. And if you want to codesign on Windows you need to buy into the license cartel, although I think @aaronventure found a cheaper solution.

      Apologies that was lacking context, I was referring to the Windows codesigning!

      Just throwing this in the mix as it seems to be a relatively recent article with up-to-date info on codesigning for AU/VST3/AAX on Windows (Azure) and Mac, as well as the fees associated:

      Link Preview Image

      One of the last pain points before launching an audio plugin business is dealing with code signing. The ecosystem has changed over the years, especially on Windows, so here's a 2025 roundup.

      favicon

      (moonbase.sh)

      posted in General Questions
      HISEnbergH
      HISEnberg
    • HISE Flags Definitions

      Just making this post to reference later (I always have a hard time finding this)

      HISE Definitions: https://docs.google.com/spreadsheets/d/1crdLOkfyebQZefBQBJEXMisHs2qPkUafBtBjgVM-ir4/edit?gid=1018116899#gid=1018116899

      PROJUCER Definitions:
      https://docs.google.com/spreadsheets/d/1_1VHwHgDNo2ruV4zDOWTKKGUDnS2SOWEHOwHAZKcNHw/edit?gid=1220927349#gid=1220927349

      posted in Documentation
      HISEnbergH
      HISEnberg
    • Free LED - Activity Indicator

      Just wanted to share an LED Indicator I put together today. It comes curtesy of a script that I believe @clevername27 has posted before on a VU Meter (can't find the link) as well as @d-healey 's video on LED styled buttons

      For this one I just mapped it to a gain node but it could easily be routed to something else.

      Led2.PNG Led1.PNG

      Enjoy and let me know if you have feedback.

      HiseSnippet 2181.3oc2Y01TajbDdEv5Xzcj5tTWpKebJW4CqHJXsB33HTjCYIAnJ.RkjNewkKJpgcGIMm22pcmErxU9eZ9P9Ij+AN8Ly9xrn0XLmOebYKaX2d5tmddlt6o6gAg9VjnH+PsJUGOOfnU4y0GM2iMq8LL0SqWGsJ55NDasmMO.GEAuToxxGwGpxpqnId9ue2yvNXOKRNIMsm6SsHmPcorbpCN3ePcbNDaSFScU3dqC5Y46012wOFLik0anEfsdEdJ4LLmskz0NFGMSqx556t81lX6I6LwbSycuzdhEYycZ7MasSS71e6jI6zvbqMs2YxVfQ9nt1Tle3HFlQhzprxy7smOZl+0dxI34zH5kND9GlZifYVR9PeGa9RjSUq8Lpi8fT7IRCPhA4n0xRz5qzOkZSynmiZegX.TtDp.XkkJZdKWv7LUMuFJlWIlTEESZEoI8k5irBoAr7Q31ymo2yiQBmfg8IUSQxq1R6uldaefCO1Ft3WQNLD9HSBCyFMpifeTaupUe5SQ6m8fNoaGTqy5cZqw85elx.b1FOiFgf+4hCBH1HlOhMifNEGApEwchPt91wNj5nKiYHJCY4G6Xi9w3HFBGgH3HpybthtjfBIAwgA9QR8.ZDg8li7AEFhlD6Ywn9dHCg9v72qiNsWmdHxUvJpNhvr1n1Zbam65EVE72f43JbHhg1G00aJ0irgUHA1NDLz+xejXwLfk6ZJ7NvyokG0UnePrT7ZJg012Mv2C9v3IpL8jZ6oJOeImr52GIhx3x1cxD9b8D4HbdDhkJXmtsa8hKF1ZbWPnFar6dH.P5PrvyQCAyMksQm1u+3i6c1QWbXq1i6ODX1D8WAA1YagDGReM.cQt9.jQ8lB1iSblvG+hQi6Nr6nditX7wvuOt+IcDyVCEYmMmaejHXCUHb5DOnaWNylaCbyMaPf1wgg.XfdNmun0pFRlhrhCOA1MbNIQwEoNr.UoYRrKQfBCkKkXZOAPPzQeeOo8Egl3GpX0qUkuG3.7bTLUXY4Z9libCE2gLAbPPrq8AWTGHxSnZJO7HvW5ukBkN9Wyyjw2gU8D3azFOgxHtx7bJdFynSmcWDoYhaA0ygaLYN84lAQnFCvDpKzZcDD7BoYpg9o0phfGGeKrSBQX1NECdfgXuoDCIs57EMDnuQC9TwkHjvhC8PRKHZCW5qKQ8.uuIam+RpUQjIyRWzzs3b2SkYiHFNjGv5Y+wy5E5D8WPFfVgXBwm0PqiRjCdK6EiMAFZlQol5Za.1i3fxy.Xz0wgFDQPWSYyP1y8vtvZW3fTDAfLOE1XiHrAPXNaneLCvCiT7vXZ1Zk6PhgEIDrFwcDNgu1elermcjQ1xiyjmenK1g9uH1hDpEfkhwJKBOb4sjgpoNfk4LIdodteZ8aLoR2RtB4wfPx.YFs+FpeLOU2zPrMkmMHZF11+ZQnCmqofpQDAqRomxAliR39PnTAiWl52wQ3VNAyvFYTlAQF7kz10pivur44nmhZxeaS4aRUVxy6VkW5.kcvUYSgJabN3xHz75.sM2YaNQSIwMUHxBiImmBoS2XBX3INFFu7lZoQIJQRKggcqmOvtmW.Y644QxQtO.HScKVt+WDwLO+CAsJpsFar0B3+cELLaVBZjPLgEInuY5GEAj1DtypvuzA9+OKuns9YgIKnNyaCTrCwW+wDTpiZJioeC+GUYb.PTESariykfOcdBFH+RVjOOz8DxDdAP4ElHJlQtCKxZXXVaODpfLC4X8sKjnNwTYBH3WkcPd5jppzLFFlvfXFRz.cBxPQC+875HRWK7G0hKx4dOw3uAQbfjzkw756qTdUB2het3TOTYpGV1TOTcpG9dl5gkO0od1sBBblKOc7FmklU.mf0EpSprCTKvS87Ue8EpYr1dKp0g2AsNLWqCKUqB0V3HPnbd9IfPE1HtaqnqhQDlnAAlnDcvMleNsvQ1PTjYs8xJbv26LeFoO3PW8mptZUXS6lCMYRoiwqYOz2wAzYYCKmsaQPCuX2KIPhOQIlYLBMeUriN82cGcpMbZIagPgQeuddTV+.h26pMTsj9Nf299dcvLLuMvDZ.eAjPFkaBU5PtBZBW1T3p5cHQuh4GH3MoYEsJ+NlXz0RaYjWgiFEZL72qqtco8Z0d6mq9wLBOXMmxKNPohUf7id6+V6s7AxIyo1XcIUsE6eE5bV1EWw1oEoaxZuqPOrbWSuHJat5kR7QqG66pI9k5CnLqYkaiKUhMB6a+RXiI2LwZ5xxvxMvUzO7e9I3ZHdb18HQcCbHGkN8etddGt2x0Ioo0g3fK.S+.0lMS0EqjKbpm2Ufi+.H+T3MA4kuyqRyaeUtxc1SPTK9CZu0+ft.lenajhs9G1F4WnCoM+UxDkcPNE54KjB4n0OK1cDjg0h.lGztfC+jnJKwOyP9cC92biXDzJr3i2BOICZx+tRxfloCpBCesDF9L8e.eEQbAVBD3OJ9F5qyEcDAZRgiOleLtw3f67MF22hAlvXnu2H9kDZpp4QDW5X3.unBTugDMKUhlEyKwh8JpZIoBbwghCAnPguGePJwlpDA2lBJC9tflNkpbr6+4655gAmhQDXka2OxB.FNNqhpChgpMEQLlpBlStoJ4iwg1vdn0skqbk6Vtx2y8l+nGb2a9uAJ6nvYpqlZixyT6BG14.U5kkIeBN1gkRsXn2o9d9Ay78nE1nGRfLFSmRJ3+T5BpEiAsPlS4qNXHwgfiTBG+yGbB0ifCAbhbOwByO3+LGkte8mzklKhmu.8a2xEW9+qKW7tG8Ao.eXuK905Pp3lHwVI5Wm8xOE0.7oXNbwVg9WXI6ylGz+XAEXc6I96.uJz+.7MxTSz6sJX5BGYbgkUQUsffMuuBt48UvstuBt88Uvu49J3N2WA+12uf7SzaEy7StLAnxlAcEWbQkJxhZDgIZ+OvkIPQf
      
      // ========== LED ANIMATION ==========
      // This is mapped to the Master Gain module, but it could just as easily
      // be repurposed to map any other function (modulation, MIDI event, etc.)
      
      //Timer
      const var t = Engine.createTimerObject();
      
      const var PnlAnimation = Content.getComponent("PnlAnimation");
      const var GainMaster = Synth.getEffect("MasterGain");
      
      const DECAY_RATE = 0.9; // Decay Rate
      const SMOOTHING_FACTOR = 1 - 0.75; // Fixed smoothing value
      const HYSTERESIS_THRESHOLD = 0.0; // Fixed hysteresis value
      const SPEED = 15.0;
      
      // Current Values
      reg curLevelL = 0.0;
      reg curLevelR = 0.0;
      reg smoothedLevelL = 0.0;
      reg smoothedLevelR = 0.0;
      
      // Last GUI values for hysteresis
      var lastGuiValueL = 0.0;
      var lastGuiValueR = 0.0;
      
      // Define two colors for interpolation
      const lowColor = PnlAnimation.get("itemColour");
      const highColor = PnlAnimation.get("itemColour2");
      
      inline function interpolateColor(low, high, factor) {
          local factor = Math.range(factor, 0.0, 1.0);
          return Colours.mix(low, high, factor);
      }
      
      // Cubic interpolation function
      inline function cubicInterpolation(start, end, factor) {
          local factor = Math.range(factor, 0.0, 1.0);
          return start + (end - start) * (factor * factor * (3 - 2 * factor));
      }
      
      // Panel Animation (Ellipse with dynamic color interpolation)
      PnlAnimation.setPaintRoutine(function(g) {
          var a = this.getLocalBounds(0);
          var normalizedGain = Math.range(smoothedLevelR, 0.0, 1.0);
          var currentColor = interpolateColor(lowColor, highColor, normalizedGain);
      
          // LED Effect: Outer gradient shadow for LED glow effect
          g.setGradientFill([Colours.withAlpha(Colours.white, 0.5), a[2] / 2, a[3] / 2,
                             Colours.withAlpha(Colours.black, 0.2), a[0] + a[2] * 0.375, a[1] + a[3] * 0.375, true]);
          g.fillEllipse([a[0] + a[2] * 0.05, a[1] + a[3] * 0.05, a[2] * 0.9, a[3] * 0.9]);
      
          // Inner LED glow
          g.setGradientFill([Colours.withAlpha(currentColor, 1.0), a[2] / 2, a[1],
                             Colours.withAlpha(currentColor, 0.4), a[2] / 2, a[3]]);
          g.fillEllipse([a[0] + a[2] * 0.125, a[1] + a[3] * 0.125, a[2] * 0.75, a[3] * 0.75]);
      
          // Center highlight
          g.setGradientFill([Colours.withAlpha(Colours.white, 0.4), a[2] / 2, a[1],
                             Colours.withAlpha(Colours.white, 0.1), a[2] / 2, a[3]]);
          g.drawEllipse([a[0] + a[2] * 0.125, a[1] + a[3] * 0.125, a[2] * 0.75, a[3] * 0.75], 2.0);
      });
      
      t.setTimerCallback(function() {
          var GainLeft = GainMaster.getCurrentLevel(1);  
          var GainRight = GainMaster.getCurrentLevel(0);
      
          var peakLevelL = GainLeft;  
          var peakLevelR = GainRight;
      
          if (peakLevelL > curLevelL) {
              curLevelL = peakLevelL;
          } else {
              curLevelL *= DECAY_RATE;
          }
          
          if (peakLevelR > curLevelR) {
              curLevelR = peakLevelR;
          } else {
              curLevelR *= DECAY_RATE;
          }
      
          // Apply cubic interpolation smoothing
          smoothedLevelL = cubicInterpolation(smoothedLevelL, curLevelL, SMOOTHING_FACTOR);
          smoothedLevelR = cubicInterpolation(smoothedLevelR, curLevelR, SMOOTHING_FACTOR);
      
          PnlAnimation.repaint(); 
      });
      
      // Set the timer
      t.startTimer(SPEED);
      
      posted in Presets / Scripts / Ideas panel led animation
      HISEnbergH
      HISEnberg
    • RE: LAF Collection for everyone

      Draggable AHDSR Component

      I've been a bit of a mooch lately so wanted to contribute something myself. I've been revisiting an old script posted here a while ago (can't find the original link) on creating a draggable AHDSR with a panel. I really like this script but always found it was a bit difficult to bring to new projects. I didn't do much other than organize it a bit and make it so you can attach the paint routine and mousecallback to several AHDSRs, and I hardcoded some of the values. Still a work in progress but here's the script:

      *Edit original script, courtesy of @ulrik :
      https://forum.hise.audio/topic/4541/paint-routines-adsr/65

      example.gif

      HiseSnippet 4331.3oc6b0DaabbEdos23P4TmDjl3dbhPa.YLkL2kj5mn3JIqerEhksfoyOFBBIK2cH4ZsbWlcWpehiQywdqmysbpG6kVTfdSnWJPQNjhjC8TQQu0dnHW5019lY1el8OpkTxNxokGj3Nyady68l26MeyOb2x1RE63XYKTXh6eXerPgWPr4gltcWoqhtovFqJT3hhJc0brEtwg8UbbvZBEJb9aRprPwKHP+7sKdCECESUbXQBBumktJ9158zcCKcqkdGcCi0Uzv2WuGG00WZCUKyUrLrF.Bx4EqJzWQcWkN36nPH6bhB2RwoqPg2Tb910letYppTSaNUrpZ81Mlcl5JR0q0PAOes1JRp3YaHqBB4yslltqkcSWEWriPgKbCKsCa10ZeSVG7d5N5sLvjGjDZB8Lq30sLzHpHoTgU5panskuExQ.X5Vg1qyyrW+PwM00zCJOzt8RzJPgsf2.V3bQEuyGQ7j3EupbhWJhTANQ5BLQ5kEapZq22MrFh7bIwMLcw1sUfwIdQgQqv4lsj3JV.EltS2SYW751vCAsnzLUqVAUqQ0xKLwDS.CVNtn8TrQ6Z1x4CWybOz0Q9MtC1cYCiUr502xDd1ozj.QDZlDZaXK6qXhMtos0f9NCsw8MMR1Xr4ddMEAMl5v50zMszFXn.VVnoKS7ai1vcMsZEzoauCnLlfKlC3tgQPufMr5im3QEmn30tFZpT+Pq51KuN58V9dar7Mt8ZMQuAh455LzlEJD2V2D+95ZtceOEiAXpJHOc0EPHdhfPJil5eB1mFfnZIHZcHbZMSEvSQCgXDIs.4+w6tikHR24vSUHQIjJvmUWkoxTBqdv5qOizLUWs1Bwo8VV6gsCHkQ6JqrN8SDhUTc02CynTxWLqdv7M7XLJIwb7VJFmiPstYBlSnVpZHqOtw62AbaPT+FcyNCkXcSCvbiZOvD5SKSDD84BNVDF3Tp7DEezDEIBfgkphAxbPu.mw9J1NXHdqjeH0zFXyNtcQWCMG3BSaUaKaTIchzu.RG81gsGd7pWsLiHudnHqK5Pp2yWuHx+CkQOjwnGBLZN3eAL.wwkvOTFMc+ANcCjvs0QuIZNzUQObmx7b+wbeOLfi0VJa7oFH7wDieTizBG+3wMMrZAJ2VjbHve0McQ2yZfKX4GoAGubKaY6MxPZ6pXHkWOBYLSmtoF9.nJNy4Fjh.i2TRfnVL9fBWhMugP+QGZWPsJ5sQkb6pCi6Wmm9s02orOIAzFZ886W8EhTWKarxtAEAFThQsHoK3aEQbCXtM1cfsIj8yWu1GXKQfHYQoolnCCdU1kqxag06z0sTYzTHoPJ1iiBZBK9la0tsC1EnHRJsv5M7SGBjDM0XHM8UzzfPOfhFgE1xx00p2VAUIWMrNGCKRWVZePPkA2TOFTFBm33vAgeUqEmD25g9gLdkPmkfT1jK65BXSlrBZxaAXEH+eUrpxgjuzbfiK3KR958vFXEG7jbrfD0bWaMrMgMvjoxv7oUP0qfZDgHHD.HHLrY6vAQN55yMmoJL96h2RwePq+zpPmaWxOJi8Whqssx9joNoyxgTGXuG1qx9SCRts6cv62bPKJm7rWUfA+oBr9SEyjOEp5zM76FhmI0p+ln81t5NPZAul4U+daKsC5mB+q9NnE4xPn0hLN0M53T7tpLTUIInTBWJGxazacB3TcdNsfuk3iGnnYSlg69VkN.bWj45sqQF0FIYM76yrSYttqB5fJf.6a7.ONV9wsoEGjO022bYPyndnLK8U4L0x6DH5jHIhTmGNGkI0HBprWUmF1QOAmxJ3OSQzgLLwSwKFf4kQN72fNXVvzkSCFmWX8TFbGeKTicNdwuwNCMdgpB4NdJhXMjVsSz3bZZXMEWkoAFfUcIYSfuEYXQAxXPfbPRWeCqAlZNkjhxkNS2B2Q271JGhsK4ZO.6WcmoaCXNAf1k7y2WZxVcXPqlrbHUPFeVgbzo6h6EmRxzTbnX4gfv5JV1nJTYNurWNJ+4..Gk+vn3977uR3bQom7zsqkCF0hfUNj87PmihACkFnsF7f0RGqkNY55vAxtDftnEin1w.WWF8VIplGmdD7YnPOgVGvbO.jGjb1S4MYcVTeXH0RCgZJW2DLrSaqX1AWpE3AGj2a+f1QR4D5WmTDocGOaNjiMcyfMIxSkfsrw80LLz66fKsMQ3HbNjY7eGhnkSCb6iSy8vPoEfGc+tXSDcLiHNzwNXwHt1VFg9LwGbecO3YwcG7.yLnca8CVHhuaZQ.t3CbiGfER95fPTZRFzYx2AbJRQ0MerV1J8H67BX8o3d1NlztSx1PpLXvhjQoDEIy1AfdRvic3vJFQHhaZfPgZfyOyF.8wjHsaLI3tyUPOmISJRrAiqyoNWEM4aATeUlzd0TMqDWikMz6Xh0tOXMKQ4REJfM5Dhv+2GbIpB9ESpBnurwZ2f5tMY7E0LBqfYSqAPZkUfX1V.1xSxRY1TktTFpmYlqlARf4.qxGZkaWJNPaPQXqCg4YV9Ys06f3mcK80xDVeZKmIBAouZFtvQroCc4FUiTbz0nvk0LskoDl7HLoPJKWKPo8WwVXIgsCuGYY.rnk23MPuN6YvStSlYTTMfIxbHqPRpZ0pSWMY3CjleCsCROvrGwe8CfFy5ogQ0CBn5vj8gqlE55IQrrvIa1SuLR5sa+A9ojTZ4TJXCVfdkMcWY5rFdk5oJkSNeAwDy31a6a2JGmlDBA8SnUl19ERkHlg1yMOZUONdAXCHOQn7.dIil.QZZfAwyNPmHOlY3PXoJuMJBkLwLSxKmZ+cbJXTkxmPXvNOrSZn1qGOroTxx61F2mrYRk3xkGQBectPsThsR1OSIk29Hd3rpgt5tPV5O8SQCMdNQKxQrw6X1hlWyIwFCh7fybS.8CYxA19QPBB4mpflnJcVerafQzFEO7l17XSijGG6.MxeGJIfOzCwXbGK6dJF5eBVqTJQ3IhyBGH2y2NEzCYsSmbe0YecW0tIP4MzQFUXNYT02JyvV1XrSW81tqZsuYnegMYFrUHi8oGz7nrCkRePZ6Y1g.WLtcKtMAHCvPE5a9AWiLuX4EFmtSsKAfuVoLZ8iyLuwojFWMeZb0SIMVJecGMUaX28fws6pdrF3iQXGqgGeXZo3lKktadFBfb9rVxivfS18zwnpCQojycr6oUb5r4yvL6ojW6re2GmVKeZbsSIMt1ouqesQx0ud9z25iPhhr6ow20u9HoTMxmR03jGO2XzUpGOFKhmd5vqv1hmfUwORqd2xb4asZy6AXLb73SIU+6.QED0f3cBkYrr73q1e+t5..D1Z9Ia9Df3zgBgyEfKzgsyTDi1zorfdtirMq0yG+7i4r+.nK+VwNR4zVdOIoXj1r8C2gtZFektbDxi13icCBRYWBB1pfneI1pte8HGRZT1j6yTsX.VvQ.QLizfyl65g2vkLIlseMQ2OkrnUwEF2aMvkg3WtRsJ0qznxLUlsxbUlOE1lwpCB1sykcbz63s8QL+UGNBndG6x7N1kc6B1M90SfMyfm9RxGrruHVJTX2d2cpf71HwL2uPNAlG7+tCG7uWbNswAoiJwwKeBYTvmFIGIF3rOQ1nO+jCPrnEZMEHDkV9PYW92usGwIwgQifxQugDd6pXI+jNkRa0jb2HBeCUDCQD1R21ReUJjuzb1g7tXQt8ljmoilcL0jrDy3xFrLvNHcSlM8lrkmNB1zLS48HNO5bluKtIMddN1N2S0i.aWx7+Q1M4GOwDSvMWwcrbw20rT4IftApCEup1sSsNONafsSsZxUxzdXMrj4fdsv1AyG4QnPgKD8tLJl8cYj+pVpxt9BbDZYtgot6c6iMy5BXJ3cmGDDJ7RdREPpK8VOdYua8XSCcMrsftF4tY5c0CYWZChFJIPkdu6d5W7U1FKIjCN8pQ4zsAebinrZoZ0t3Q4gUW1mUj6ORTdbzq7K+M4RbdEedPu6II0qiJ7aWLWL5k8Yj2cWIl44a9ZoiFI13cuWhwl+laywwJuB4FpDSw9xu7aVTXTF5oVnT3D5Wcy+znwoPmH4PN8u+Ovme1GM1NQbrp+F+8+H9ubkQ2IRNhhIH7y+5wzIJFi9ra+GFKmH4XwX+ywyIJtZc3udrchhIQ8u9+XbchhxoO62+u9yKJ7tarJrtExcw1K8DjxpO11UmjMrvp38zUwralcQwUwN65Z0Wnv4BtjyBElfIA+.+6sMYlVp.bEQxbmqv.vis8ugxBGveS6Oj+g8IG3UPAGYuTW5IbETxRCVx+5Z.8qnzLyN6rxRMDBO5XxOAf5xyWe9YlUd9FBb2pBVMyNmz7UqNWUtZXULmzbyVu9byA8Emt8BYoaSH5gKP5DnNBOXo9Jj8jlqGyxnkpdmP8BTbXtYq8C.rIT3EEIHMBKHGVLVOUu97MpMCYtq.Q7Ri1TVb1me7Rg1mk9nk5YoA744Do+dJ5ob.eN.GWbex0sj+GpQOcMMC7VVN5j41C4zuXI1ggCydKh5wTNxfFSJPtD1maCsPxg+bOkZnFbTqnpJSjtruHQoWfo+jvJU8Vjm4+co7fi3sHvmXVju3y+7e2hb5s1MDxv9HHezXo8WJun.xXD9yZ+DdD94DI8uvorxkF7jLzviLdBqgWTjJDm1duIANkgmKLDxDjhhdj9rqSaRXdY429wOgGUKJ5IBi23ZwQDBZFisfyarrRT58FfeMwvMdXKrM41BozAK3qNmW7mj4H2ecwSY0JU3vY4w9wmszpLQG8iRuwQTL6kdJCOZtpUkaTEPAk.dTcYo4qMu7nBOR9TFdTlVsmUwGIeVEeTlV5wGfj7yP.jxk5eo7tD2yZHjNgZWZK99LGDoSnCbx8E3YBLRmvQ1jaiwYPPR4RGKNh6wxY.TRmP8J0M74L.LorUqj+5+eIwfeC77uLBHuxF7pfza7uA.H+J+MAo3PdWsSs2PA4UDeYwsH2ZtzkwykhLJH7DQF8duN7CDWqcarpan.dAw0+fmLuDG369Ky59KI1DFoouYCnc9qQeF89J6gQ2DC9.DimzPdob7U48kxQ+b+R43tptP2eeaESm9VNQXbSbO86CNqN7E9tN30swe78Hil7kuhE45CmRUe6hqCJYpsooh6.apawx8rFX5FwM37C6c8gzX9t93Bm4dWe7LPvdjWGIuHSFunH8vMoh1yKReobD0wcSKSq9csL0U4ko6g8tqJ7ElpzyMID+wDElCm+HeXDye9ebvq82VexY4Qg8wctbLLRbmulOTEtyJye9Vty8ZMUqMYXYFGqszvs1h4ziHXgJzP7yd49isnkydB3KvfHeV098h93lOyZ.urOVzyplvgB+37e+F9A6pA0YSEHiKYMD2YPulvr9pXn2MMwFjK0QgyQl4l8bUxyro8M0nOPNEbuJkHOWvqRI+J4Uwq3CwgflIDhyqRetM.a9rBBGoTg3DozXsPN0VDozUwtCLixZVQQnhXJVGLEbz87K4WnLegaoXFgYvyQ3zl5Gvu1B1OT8lXPy0tqiJXXH1Y9PusFX3v9QvKw2vvhk4K9VJ1ZvXn5Sd.YO2+GP1SL.YxOgAjQuxEI.jQu9DQ.jAk8Qo.H6Hx8hgCPFcCR3AjQuqJw.jQt2IQAjQuCIee.P1Exm6vvCndZAN6opvdx.p8TUTOwf1dJ6Ebx.v8TUX+eYvb4dNI.X.5L8NBdEQ.fhLhNTh9tYr7oAx3mF8QOEUaqOz+2pAXgddZIfdaReE6VTbSxyHoX24R3SO.H0GppFkUIZn731vZiaCqOtMrw31vYF2FN631v4N9FRv4t7.WqdrXC.u+VqQuNpEJ38NohDlH7espGt3J
      
      Content.makeFrontInterface(600, 350);
      
      
      const var knbs_Env = Content.getAllComponents("knb_Env");
      const var panelGroups = Content.getAllComponents("pnl_Env");
      const var envGroups   = Synth.getAllModulators("Ahdsr");
      const var knobGroups = [];
      
      namespace Envelope
      {	
      	// ----------------------
      	// LAF VARIABLES & Colours
      	// ----------------------
      	const var LineWidthValue   = 2.0;  
      	const var BallSizeValue    = 3.0;  
      	const var FillEnabled      = 1;    
      	const var LineEnabled      = 1;    
      	const var BallsEnabled     = 1;    
      	
      	const var BallStaticColour   = 0xFF6160D3;
      	const var BallHoverColour    = 0xFCCFFFFFF;
      	const var activeColour1      = 0x956160D3; 
      	const var activeHoverColour1 = 0xFCCFFFFFF; 
      	const var inactiveColour1    = 0x106160D3; 
      	
      	// ----------------------
      	// Knob Grouping
      	// ----------------------
      	inline function sortEnvKnobs()
      	{
      	    local numGroups = parseInt(knbs_Env.length / 8);
      	    for (i = 0; i < numGroups; i++)
      	    {
      	    	local group = [];
      	         for (j = 0; j < 8; j++)
      	         {
      	              group.push(knbs_Env[i * 8 + j]);
      	         }
      	         knobGroups.push(group);
      	    }
      	}
      	
      	sortEnvKnobs();
      	
      	// ----------------------
      	// Global Panel Paint Routine
      	// ----------------------
      	inline function pnl_EnvPr()
      	{
      		// Determine group index
      		local groupIndex = -1;
      		for (i = 0; i < panelGroups.length; i++)
      		{
      		     if (this == panelGroups[i])
      		     {
      		          groupIndex = i;
      		          break;
      		     }
      		}
      		if (groupIndex == -1)
      		     return;
      
      		local w = this.getWidth();
      		local h = this.getHeight() - 1;
      		local v = this.getValue();
      		local offset = BallSizeValue;
      		local lineWidth = LineWidthValue;
      		local padding = 5;
      		local bottomPadding = 20;
      		local slot = (w - 2 * padding) / 5;
      		local x;
      		local db;
      		local obj = [];
      		local names = ["Attack", "Hold", "Decay", "Sustain", "Release"];
      		local knbsOrder = [0, 2, 3, 4, 5];
      		local knobs = knobGroups[groupIndex];
      		local p = Content.createPath();
      		p.clear();
      	    
      	    // Draw envelope curve 
      	    p.startNewSubPath(padding, h - padding - bottomPadding - 0.5);
      	    x = slot * v[0] + padding;
      	    v[1] > v[4] ?
      	         db = (h - 2 * padding - bottomPadding) * (1 - v[1]) + padding :
      	         db = (h - 2 * padding - bottomPadding) * (1 - v[4]) + padding;
      	    p.quadraticTo(x / 2 + padding / 2, (h - 2 * padding - bottomPadding) * (1 - v[1] * (1 - v[6])) + padding, x, db);
      	    obj.push([x, db]);
      	    local dbA = db;
      	    x += slot * v[2];
      	    p.lineTo(x, db);
      	    obj.push([x, db]);
      	    x += slot * v[3] * 2;
      	    db = (h - 2 * padding - bottomPadding) * (1 - v[4]) + padding;
      	    local ddb = db - dbA;
      	    p.quadraticTo(x - slot * v[3], (db - ddb * (1 - v[7])), x, db);
      	    obj.push([x, db]);
      	    x = slot * 4 + padding;
      	    p.lineTo(x, db);
      	    obj.push([x, db]);
      	    x += slot * v[5];
      	    p.quadraticTo(x - slot * v[5], h - padding - bottomPadding, x, h - padding - bottomPadding - 0.5);
      	    obj.push([x, h - padding - bottomPadding]);
      	    
      	    this.data.objects = obj;
      	    local area = p.getBounds(1);
      	    
      	    g.beginLayer(true);
      	    g.fillAll(this.get("bgColour"));
      	    g.setColour(this.get("itemColour"));
      	    if (FillEnabled)
      	         g.fillPath(p, area);
      	    g.setColour(this.get("itemColour2"));
      	    if (LineEnabled)
      	         g.drawPath(p, area, lineWidth);
      	    
      	    // Draw those balls
      	    if (BallsEnabled)
      	    {
      	         for (i = 0; i < 5; i++)
      	         {
      	              i == this.data.hover ? g.setColour(BallHoverColour) : g.setColour(BallStaticColour);
      	              local bx = obj[i][0] - offset;
      	              local by = obj[i][1] - offset;
      	              bx = Math.range(bx, padding, w - offset * 2 - padding);
      	              by = Math.range(by, padding, h - offset * 2 - padding - bottomPadding);
      	              g.drawEllipse([bx, by, offset * 2, offset * 2], 2);
      	         }
      	    }
      	    
      	    // Draw label when hovering over control
      	    if (this.data.hover != -1)
      	    {
      	         local suffix;
      	         g.setColour(this.get("textColour"));
      	         g.setFont("GlobalFont", 12);
      	         local paramName = names[this.data.hover];
      	         local data = Math.round(knobs[knbsOrder[this.data.hover]].getValue());
      	         this.data.hover == 3 ? suffix = " dB" : suffix = " ms";
      	         local label = paramName + ": " + data + suffix;
      	         g.drawAlignedText(label, [0, h - 20, w, 20], "centredBottom");
      	    }
      	}
      	
      	// ----------------------
      	// Global Panel Mouse Callback Routine
      	// ----------------------
      	inline function pnl_EnvMc()
      	{   
      		// Determine group index based on the current panel (this)
      		local groupIndex = -1;
      		for (i = 0; i < panelGroups.length; i++)
      		{
      		     if (this == panelGroups[i])
      		     {
      		          groupIndex = i;
      		          break;
      		     }
      		} 
      	    local w = this.getWidth();
      	    local h = this.getHeight();
      	    local v = this.getValue();
      	    local sens = 200;
      	    local padding = 5;
      	    local bottomPadding = 20;
      	    
      	    
      	    if (groupIndex == -1)
      	         return;
      	         
      	    if (event.hover && !event.drag)
      	    {
      	         local closest = 1000.0;
      	         local objIdx;
      	         this.data.mouseX = event.x;
      	         this.data.mouseY = event.y;
      	         local tdo = this.data.objects;
      	         for (i = 0; i < 5; i++)
      	         {
      	              local diffX = Math.abs(parseInt(tdo[i][0]) - parseInt(event.x));
      	              if (diffX < closest)
      	              {
      	                   closest = diffX;
      	                   objIdx = i;
      	              }
      	              else if (diffX == closest)
      	              {
      	                   if (Math.abs(tdo[i][1] - parseInt(event.y)) < Math.abs(tdo[objIdx][1] - parseInt(event.y)))
      	                        objIdx = i;
      	                   else if (objIdx == 0)
      	                        objIdx = 1;
      	              }
      	         }
      	         this.data.hover = objIdx;
      	         this.repaint();
      	    }
      	    else if (!event.hover)
      	    {
      	         this.data.hover = -1;
      	         this.repaint();
      	    }
      	    
      	    if (event.clicked || event.drag)
      	    {
      	         if (event.clicked)
      	         {
      	              local KnbValues = [];
      	              // Get the knobs for the current group
      	              local knobs = knobGroups[groupIndex];
      	              for (i = 0; i < knobs.length; i++)
      	              {
      	                   KnbValues.push(knobs[i].getValueNormalized());
      	              }
      	              this.data.values = KnbValues;
      	         }
      	         
      	         switch (this.data.hover)
      	         {
      	              case 0:
      	                   if (event.shiftDown || event.rightClick)
      	                   {
      	                        knobGroups[groupIndex][6].setValueNormalized(this.data.values[6] + event.dragX/sens);
      	                        knobGroups[groupIndex][6].changed();
      	                   }
      	                   else
      	                   {
      	                        knobGroups[groupIndex][0].setValueNormalized(this.data.values[0] + event.dragX/sens);
      	                        knobGroups[groupIndex][1].setValueNormalized(this.data.values[1] - event.dragY/sens);
      	                        knobGroups[groupIndex][0].changed();
      	                        knobGroups[groupIndex][1].changed();
      	                   }
      	                   break;
      	              case 1:
      	                   knobGroups[groupIndex][2].setValueNormalized(this.data.values[2] + event.dragX/sens);
      	                   knobGroups[groupIndex][2].changed();
      	                   break;
      	              case 2:
      	                   if (event.shiftDown)
      	                   {
      	                        knobGroups[groupIndex][7].setValueNormalized(this.data.values[7] + event.dragX/sens);
      	                        knobGroups[groupIndex][7].changed();
      	                   }
      	                   else
      	                   {
      	                        knobGroups[groupIndex][3].setValueNormalized(this.data.values[3] + event.dragX/sens);
      	                        knobGroups[groupIndex][3].changed();
      	                   }
      	                   break;
      	              case 3:
      	                   knobGroups[groupIndex][4].setValueNormalized(this.data.values[4] - event.dragY/sens);
      	                   knobGroups[groupIndex][4].changed();
      	                   break;
      	              case 4:
      	                   knobGroups[groupIndex][5].setValueNormalized(this.data.values[5] + event.dragX/sens);
      	                   knobGroups[groupIndex][5].changed();
      	                   break;
      	         }
      	    }
      	}
      	
      	// ----------------------
      	// Global Knob Control Callback
      	// ----------------------
      	inline function onAHDSRKnbsControl(component, value)
      	{
      		local groupIndex = -1;
      
      		// Determine which group contains the triggering knob.
      		for (i = 0; i < knobGroups.length; i++)
      		{
      		     for (j = 0; j < knobGroups[i].length; j++)
      		     {
      		          if (knobGroups[i][j] == component)
      		          {
      		               groupIndex = i;
      		               break;
      		          }
      		     }
      		     if (groupIndex != -1)
      		          break;
      		}
      		if (groupIndex == -1)
      		     return;
      	   
      	    local knobs = knobGroups[groupIndex];
      	    local envelope = envGroups[groupIndex];
      	    local panel = panelGroups[groupIndex];
      	    local attributes = [2,3,4,5,6,7,8,9];
      	    local panelValues = [];
      	         
      	    // Assign the controls     
      	    for (k = 0; k < 8; k++)
      	    {
      	         envelope.setAttribute(attributes[k], knobs[k].getValue());
      	         panelValues.push(knobs[k].getValueNormalized());
      	    }
      	    panel.setValue(panelValues);
      	    panel.changed();
      	}
      	
      	// ----------------------
      	// Assign the Global Panel Callbacks to Each Panel
      	// ----------------------
      	for (i = 0; i < panelGroups.length; i++)
      	{
      	    panelGroups[i].setPaintRoutine(function(g)
      	    {
      	         pnl_EnvPr();
      	    });
      	    panelGroups[i].setMouseCallback(function(event)
      	    {
      			pnl_EnvMc();
      	    });
      	}
      	
      	// ----------------------
      	// Assign the Knob Control Callback to All Knobs in Each Group
      	// ----------------------
      	for (i = 0; i < knobGroups.length; i++)
      	{
      	    for (j = 0; j < knobGroups[i].length; j++)
      	    {
      	         knobGroups[i][j].setControlCallback(onAHDSRKnbsControl);
      	    }
      	}
      }
      
      posted in Presets / Scripts / Ideas
      HISEnbergH
      HISEnberg
    • Modular FX Template - changing Fx order with drag and drop panels

      Modular FX Template

      Every now and again I see a user posting about how to rearrange FX networks and a new method crossed my mind on how to approach it. This essentially involves reassigning the processorID that a group of UI components is attached to. It's really experimental at this stage and it's an unorthodox approach so there is a high probability that you may encounter issues if you use this (just post here so we are updated). But anyways I wanted to share with you a template on how to do this.

      Important Notes
      • Keep in mind that before using the patch you need to export the FX to dll since we are using the Hardcoded Master FX to load XML Networks (Scriptnode).
      • All component attachments need to be scripted. Since we are reassigning the processorIDs it means you cannot use the Property Editor or it will become detached (and probably crash).
      • There is a degree of flexibility with the panel layout and FX amount. INstructions follow below but it is quite easy to adjust the grid position and add new effects.

      Snippet

      Alright this is probably the least frustrating way to set this up to share on the forum. I realize the effects won't be shared either unless they are in ScriptFX. So instructions for compiling:

      1. Import Hise Snippet
      2. Save each individual ScriptFx network (6 in total)
      3. Export the networks to dll.
      4. Select "Replace script FX modules" so they are exchanged for hardcoded master Fx.
      5. Close and reopen HISE
      6. Uncomment the code.
      HiseSnippet 7052.3oc68zzbbajcfRDjRi9vRVZcxgTofYrcMzhdL.lushrFJQRIFKJSSRaKUpT3hYFLjPDyfI.XjH8VJqOkZus45daqJUs+FRtoe.6AWkOr2R4jpRU4n+GjzeBzMPiY.lYnDWsb1p7Rft6W+5260uu5GZsoqSKSOOGWoYxsyQ8Mkl4hxaeTO+8u69FV8jVeEoYtfbWm1CrMbW6Po6bTeCOOy1RyLyYuGrCyb9YkP+94aeGCaidsLCekjz23X0x7AVcs7Ce6lM9BKa60LZatiUWldWpw5sb5cWGamA.j4rxpR8MZcfwdlOz.1syHKceCu8kl4ikap0R0rXyVFFMMpaVqV4lkpUstgoVaspkqTurZ8lUJWsRKoYla01V9Nta6a3a5IMyr2wo8QauuyK5gmfuwxyposI7AMosAyL90q4X2FtDguU5t6aY2dSJUxSB.kMCoYmESytt7FVssBdeHs6JnFTBGAKAblyvidmkC8zXQOUFzS.JMCCJMKFktp71sbs56G1BlWtdOeS2NF.9DKpf6qzY9AE4O8iycqo5ubafEeTV6QJaejmuY2b4VY4cV8yTH+1v3HEcU8x4V9q249e4Vj2e+02d0GdmU25d4dv52c0GtMo+ar9NJ4Wek6s7ZKNswyO9Syk6tN.5SO+BcMNvbMWvCAzq7UqptjRoZpKdyb.Zzm9oJe85JOv3HmA9JfQ44azy2KWK3eo7bvpsuQOS6u0ps+9J2RQuj5Mi118Ms1aeeXiZwabafvuUu8.sVlss8bsZSgIyDbCtAEc.OFzY94G9VfTmq+ifSOWStNuHbtEfXdaZ5tkyK.MV7l4fTgU6zwrkuxJlcr5Y4a4ziYDqt1Zqd2c1Fz4mjCx79UVs+LkE1x74ltMWXID+rskWeaiifaxi1jxA8bZtoK.vGBZ5fdM2EzLss8M7Vtmg8Qemo6mozwv1y7kKwLGqXB.Jsu7yAWSwmCPyBmCe2A7SwZV1.QCgKCtlhOELMOho.nQcOJfhLErMIXJBad3Swcc512Eof.zW9ofqo3SArYxzO7oX0t88SfWv0T7ofoYA76bOEKAB1GBwEmdl76.gf6dtNC56AE.eJqnrAATwaYsC2DIlGqgl9871c0dF.Mxf1n5I1yzeYa6voO+Bf9Q51BKFY7PYKfw0V7iOXv3wR5CbvvEGToosiuxVlcLcMA1W8T3vVXiwQV7lR11bM2SoCp2.HQeIy920a6oj+gl9uvw8.EH2A7XgC6Zu3hw.Krq3w2wwUIuE3A0apXo72S2uWv1r2d96Cd2Mtwh49UHQgfg9DqmBF.omfGJX09l4dIBUtmouh+9lQXnYXRB433YIQtDyzGJzAn3Pff9OVcTX6Diz2hXI6bTqWAhRwlRFFaG+cW.nklaUCddgUv6FVfL0.x.7+iJCNLH1umcbPB.yKwb0sIjx0NDK93FH9DRO0PzyaMLBJQ95IVehFDUPdFBQD3aW6Q4W.2NDOrHKAFIuXiB2lnQEaxJ3Ez8fUH7030XtrHTfrpY1AJzwhaOEOyg6IvTaPOohiHl.jRNnuxGQL0mypmsUOSkNC50BZrSAYzyv156LQcOOxH4RJlDw8k.cns4gTbA0Zg1F9FEn8.Lmz+7lL8Ar9+FC6Al4wiORS4Wvy34lq2CH3BdBngDpzMdmdAz6.PqgtJDuO6ibCg1IrSIQ6ETDz0w9tF11MANkm2oGZwRdsXNBUHVDKIJQiQfeINMEKgjP3XGNt9PmSRjg.LW4ig1cNBSAMVRoIclcAbS2dJFPwQbqKp7IJMYdDMcQApMZtvfMOEV1NsLrQymYaNiFARzoilvqOgEdE5OvaeVpSfdhfofq6vGxGi..sjHBa3F5PvH75zE4z2FFfsxcrcbbAP5S4bIjfaginuiGxgv06gcXzR4C4FPz9e3lNPJHiyo2HBL93PGfiN3iBG7iAiyE067rNZeCFeaYvUgDdn5TztiCAaLf3Ey.D1OnWJGE1uWBEh3EZtIiPLYiiBcCUL4M9MXTkJOGwM4k9L5XhXxTdiKzuu7ndtjBHfkHhb.gcsn14PcV48uUHvhJADQkTX+BoJ3MVwDOiRCvc0wssoKdetW9fM3q3Z.2WucOi9JOvYOqVw2aCZZGm6A3wXRBOs.Hy.1F+HZnQv8zPFHYVY6yi46yQh5ylDIOdBK9OcFzqc97z46SXDYWDroHPFcQDGfKroPxOYOBpwk82AAKF8G3laMvEX0lEWBP6.0Ub7R556yC1IvrE3S0ixXirTuwsDrAEyMGFQguszH1MNJFgKOVOideBxp7QeDqGSrZ1u0shf2KF.sP3FHfGvFRRsaH0f3nFDkhLTx58yUTEqF0A3UlKVKvshLsOQ8ogySX+B22EQZfAqhrAMxhlkOBlOygu6dHCNaaoyhGArLP.lrgy.Oy.2Mn6+ya9bvxWjbApgBsrsZcfY6E4Xs96a4g83xxCpgYObpMfNLQrKxCj1f9jjXBBVA1EPOQUx.1oQFuosuwiX3M7C7HtAdTzA9XNQMAnWWHk4q6OTLL9pEEvLOJwnKENn3h3A5kIQHtElGCAnXsyQEBHr1wYuNixQv.YDNhZ1mwKZA9VmjGI7puhpRcHJvBfGMVEJDYCXgN6h1eBBbilP10aCDEXhEJe.1cCvbwGQXbZHZ5GMIjnxX0PxTPTVHMk2ku87Kl.AFFDRPDT.eyKfBK4K6jOxDHxwJjvKCX.Jt+DsjDfYhGiYLOMgn2hMWADqCvDqC.DKlrBvBx.h2AThmXLJjV.AD.rI.umbPDjhlQh6azqsMLAAr8NOrkkPsyH5EQswKSdQxlfBtUESpJfVFCSOAauF05U7nvBvq.1gAjb+G19KeXAOen9.qNGkONLf+VXSFo8OiSZ2BIluj3gglOv.TSn80brscdw253dfGvgdyEHoDLVmeYxTzWF5nNPG2W.YssHtkS0nEMYhTFYP9rc5gSXMbzKQdCJ8xru.msWt2fSNK6qBy0J6aQ4+D9BTpNiGjP3rmuEM0PBCSvhr+kQzU8og6goikPrfxVVh1pxr4TEIMrrOP.n4.eRZJnysvXnYHMYGa0lLrUaLv1P9V1QW8ICc0GGzMTnJ63awICeKNF3KuDe1Q4RSFJWZLP4fsiYGaKOYXa4QhsYHgnnt9LbWeFu8QqPqhOKtKEbc7IO6ohRMHmAOlDVQbkDe9I2YfuOT8p.RLpC31GAUtIpSHeZB5HzTZnWLrdv.ORCPOoCpfqYea3o5xddM.2wVHojkkBOtvAnNTuqfQjFhOI4CDumNPpL8r5y+9OORdWf+Z5ZZbvMEYYK5pf4PrDIYvzLYhim4WVNDRzCdr4LbvG53a9k8Pd+edPqJQapSGgsQlHfXivlg0Jh6vFX9dC51zzkQRA0QoYlkuHKjStHKXqAjV3CcgoivbPZ4+k8M6kTkgHQNoFIoYVffUft5iJGiKQKGCXf.RVsg0YC7HbvVvkPHMakyHEE.Wl..LgGAg2UNT1ULb94amHb111BDqFASHms9FVGxAge+89u9uSEDtBEBqXzsOvgPd73O8u8CoBJWlBETxy3fQib+oeLUv3RTXrs02YxAhe4+9e6+SbPDmubADeA4pRTx40ajF1xUYXKBASV3J..DkqjZHbIJDPEZEOHTeQizyRffvDngMCzCFXbUJLVyzrMTIRTv7+d6zPVujLyoimhMLIsiC6VWT.btFYcGmX37yYX+BFBq4Z9OEAJq8GZjd9KFJeUFTgj.hrgSaywhtdILcE6+YFHHLD1qwRXyLfhufvfPvNmuHUT12kEJa2GH2xqW72869OtclgyJl8inUC76doBe9ErvQ31HwppiyqtBhWE56cT74JoZev6wvtRFVYgiAgxN6CfSD89c5rzqRCTdm.nrkAvwfnZ9kajMTA3ocLJbZY5WMDULsMM732T0nXweL8lgPQbDaSYlMCIDLYwLDB.ZimyALPPmCB+z2c8+yaK80qCSnDrHVItOAbopuoquEzasYVw74VsLwkz54kWwz6.em9RybtfJgA75jHgWEqW5PHbM.wY3JcHqlsiXe.UoDAu3J+lF3xhHrK+yMr7M6RKO4KJWRullVY0pUJKE1fNtkp0zpqpVSUx27PetgTRuTQ0hUfZUCv+KkJODOjsRoYP8UZvi5R8hh5MdVLTudo5pZUqnFG0CZIJpWuX0ZEU00jXK7DrmvLYzFJ2hCARqfpjALIczPG7faRW1N7Pu8vsSOkBLrlYNFJyE3oLOvnY.kwtoHJy5rTlOHJkYmXL0RMZtW3hTGvxJVsDXcJ0AHxf7fLnqpM5a.ikiA833OP5EPtTl4QVxWfHAKm+bYw09CYWYCaY94wVle9Hv81lcLFXiyAcD8CQ4EiJ7gDvxuesHX42+jXX45i.K6B8OAX54gNtcQ0NT6MMcaA5mwdlRdC5zAL8.YnOTpqU611lzCvLbB9oaiYRxxPLMcKtXQ1jvB7Ue6InE3bxPrMkqvHQcwr99GOot9lWFiqoaAxGRXBquSTBnyIiP0TYjfMb0v01O8qYVaM5Ol1HJWCpwRjMBbKSpMhJioMhykFaDW.YiHJg40rIBVtCU1M7oIv.Q7jLLcsOvh3SCyCj7XbHaxCX18s7Xs6KN0EqZeJrQTvduQlgkvEWicY2980SoE2bxnoAu5nOXvLq+1Fd9l84EJ+4aSWvxxJc8RZI23W2fcIewzlQnDVySMF57x34Aunu.9ITFfXW4R5BW4IwdWnQ5Dcim4pDjemZr3bxAS0TVH97oLwZgqveeaV6+aEUMxCEnpLv7EDRSrNEx9tqSP40rcLfk98NV1lD1SGeL3HeFEIwd9oeYDj+U1QQ9u+qFN6gDaJNPTfMpAssbPkYgGPVg0BALtvxkJWVunTr.FqpUrZc8ZwsnVSuRkRppk3rJ.OFBTnwybaY3wyDsVJV.Vk.7UJgf5hPM2KyRzljrZlPzlimmD0JqCbJnZEAqaZKSpmDkN9i1LNk4MQzlDrfFsIyiSonMiuLmZQaR.cZ15e4zm57iiHNIXJ0nCbdFX1q0Q.cs8X4mr1f9M+3nr9d+uKQqu+VhpS14JKQ2PODfjhdaqImXfQvyJ+ULVde0e7O920HjlfxsYRliRq+hwNRhDVSR0mBLXVeHtRl7gPJvXGBIyhcq2EZ2BiABLb8ACyv02ePr03CGwZ70jkKrkp6NAVpvUv2KiYCLUVu3OCpPx4+Z+INYoUqoWpV8x0EkmWRKSp4K8wz707ow70kvluhQZdMa+hmCg27bdY1mm.KXhN+voqILdzOM1vtX5OjxjrgMdQQIhRSBLlUe2iajjts0GpkrYkU9vTZbRv4nlvRcLifRzRcdY7LwsXK0HY6R7lnSGKTvI6lTlEWdJtxvyT5YiLgIJhklNtn3SeNgk6TjQd7EL7nrPeMjEZBychMQKJ1R906a+QWFqtCRvD83EgY050pC9uZBLQSaYRMQW93zD8UPlnERc.9rLDaW.QqoqU5X7I7VwKJG4USfs5DJdjoq45XqioQTmLEoRRX6Jik1uXXKVeGrfGZYAER3h67wuhy.fTTU6nJUgwnV66jnZQ8WQi6DutbraywEyk5BtIIWX9loCAgZ.DOYbq+FIZ.ToQ5Rier59IoPNmpbWAYv+UtMFAuL4L3ClYZVgHqhzs3iWtRI4Hy2bRd0+AMngTPWFYwf+6AM3Ghiivl+2aLVgkGiR71uYe1pXKgSmdBpfo5hr3GzxjZwu3XZwe1ze5zQILuQNcZLRPUvF9zT5zoisFmZmNMFxowv97oq7FmtF0YQxLiL5SYCpIgLmKIAzyIi9asDK7vqGAO9g+EAbNrT00jicEfJP.8yaHE+ZL8Jjg5yeqpBu6YIM.sBvdUlBuvN6ArNbDqvvT6pVMsn3Uk2zxu09hwwyH.GkjNVvQxET6kjweSYgH3rxq8nioai14hSixQM79HzjedZQhJ5Rncleib322EvTITVdGmMA1iy6YzsusIvGPykTZZ6z5.nDT7OJLh576.6Q9V6azCdQNLNe6XykZRh1v4Ha4L.5uwFF9tHWYd3ftaCTt1x7tDrC7tYNC7qs.+rJ7YnPw1l8Zid3+C7iznF74YHMpQaL7qOShbMNhtYfI+MPsCjvGTepKiLrAzCXQDOwKHSWOzCyKqV.7+vcaSG6i5uuSOqVPtDdPTDe4tNCf5QvX+8M71wvxFJhu8.u9.z6K6sMnynqEZfH1LOD4627qYzBPVOZSCn9iqJ2hVf1EZEHeFfogRHPZK9xfNnQHyXSCWvK8AnNdQ.mCpsKLQDfxcQZ6NiLnKv1gjlyfQl4vy1gcLfpg4vrqHStQ.JPZULtPZbTyKScsCdC4IfFfy.Qf2AJRvrRHlyNqrNZ6LaWIm3DoGxxasw1.QgW.U2fuGgPvm8EjhqumI9VMf+QBg.54ybx6AI+AnA9cn64aowAdyifmlX.xrXgWf0zm.pL1vpWvhCJDtgwgAOCKqbHQXdY7qVdfuSWvtS500MCOcdYfS7V9RIJrgaVDOkNzwQTZdYrP7HjwEOs31R6zNCyzR3cby5E.ypqYg8RZ9HiYDS2Y33TmkgSMWHm5bxehlZTlE74sChn65fmA8A8SC5jtVEfVyCLeAFmgtITtPIcc8ppUqVpVIshp0kvKMjDCCqFnrQLZDUf4bxvYLanArCEU0TKB5P850JUuppF4xSe6tNN96Cc0m.+4.9bCftzqehxEAp.oWYWRb.WhUQC21LtVPWZ7r5Utn75deCr0VF1THBHzbZIQxmZiircvXGu8T6EedGkzMcPmJd+Vr3MmzoK1ofHRIcNrf6vbkfz3XKlHmF6UmWl9AjrBW7pvQTNlUrrBc5GDwwArIeLBoBzSUEOIB.dWGlkcAAAG9ntG0RiagDcwA6P5bwIv6ONebndmHEE2BECdsgeDwaN7KDMhggTQo2n3WvG3STriHL9FE4necNiiL837wtxFNu7PCmVmKbZP.Ej7XemAff+c8BBa7z3rOtiyl9U9bhOLaJhJ1AJbamFj8aeAYKmkfria6W8DYf2MMf6SGq.uwC8z.uOMxjShAdikO0GeQa8SC69Tg6imvtupbGRUZtaan6BQDW9qXjR6XcXQ8cQNTJTz4JQ.UpEZmiyKitV8fkURBdYPZUrWFjFG07FdrdgNCjTHDcZtKvoTdyyLU1JuI54lV4AOqv47PGBr5NnaTtdlyyA3srvYNPWqJNHrD.tPg2v4ZNvlOnvdj8V50pqVoTM8RkqooUphNM2EftIk3rNaJS.BXi0DPWB2WFBzbx.28MbgGAbZytEQPJhM.WbDOEbMaYZ8byDjqIiMS5jy7YxvHUOzLCwrptnrO7C5dWO3GLL2J65AaXY5gnEGGHldabur7yP5f1s0flfHx319dAbfQnBXaHIiJYZFPFtVBBFzO38zl1Nsn.tRB.9hxa.1.ZAbuF.izrOL8BymWFWpOsGFVOErmBmqKK+08frZy1HxOudk4AFNEEbRRheQXxbBfWCzlW+BbcPj7WDXjosXmIcT9P5SZbnXdY7+hKGcvRGGSF6VgzR0mGpNJZHSWNPSlmYu1I3YIdbijDObmsY1tGrjI5G4cZmSPwwqkksM3+BfFOl+dxF3Z1r.WeDKqv0kQosBlCHayCooLLR17h9F3BDV2l3Ib0tMMa21rMtXOAfKk4Se5lp9obt9k+yjb8SRL.ZWwjq1iz8qIqWFza3uhU0KqWWs3DrX3LaxsfhngM15hZgJyl2B5M71Z33.yoXVriMfwyjWaxJjPIRqrB5JSIqFe457wAEMzx9Ivi6nXzi6.+Eue54b75rdBI2x.wNRifyn.tBDeLHY7TOl7JJjfqhCQhz3jbXGroEVV164c3cT3RxcvBnEfMIBKvCJitEPk5C9Svev3HPxIXfwibRFmB02bdnmzBcCTWudc0RkqnqWrTsp0JSbCL7dKgWOTfQtzEZv0PNZhs+oUWWstdcFr5Zx0KTqN7WEsZkKppWsXb7qRopZUqoUsnZkxZ0pfvO30VhH8ioKJq4k+DjAsXV3XxiHaxKzgeEF0o+zEd9IYKnqwLQhuGDZpD7nZUX9QJWSO8ycoDB3ib5XSRokL73HiDeZhNDd1nFRYEDSk3c.KtrZRFQG4NfzYkEu4NRhDXtteh5zBTl8XciwX3off0.DME3OCR.IqxVSI+YDfkzqomSddwT5zh13DhyLg2FMhKPiXNvD2GmWedyDhrhbjHr0SKdiS+BI9K9uPh4mvB0XMG61TB3omq8oEswIuuVhYYDzmnx13TQ8+xqDNNOIYkZiQMSDN1o2YudQxwj.+jpSvM92JJZhhIUzD3lNslIF+ZlXd7o10zpObH610IZggdcF8vfVwphi6rrpPg9KwB3Tqi9rbE9.iHdLCDtzyRD0r3pdfoCS6yQjl9vYApoFxdsioTGMOada.fkWPVeLTZmwTVNurttZpSNXlJAC3TpfuCqltkfAzTnu4Dtqcd4M2GdyIMYk.FwpbpR2ZhFj7r1SGt2JRMB.F79EnsI1bDs0i2ZchZBIcFXmStuowABcHC0f3POPMMhkwrGCUzz3ptH1oMb74a8kk6.AFsXR4Hr+0rUbJauDWQFbcYbTkedYZs2MFklW3fONjWgJ+ppx8SabKUuSR0J0bGS0J00.6uqnUrZoh55UJVudEfwjDNNKozVoe5Gq0R0bxnxkJqkREYXS8JoJTbNx182Zqvn4k0XOGo4jKOYmcQ5KxHRr93K65QJANx5ZIzqBhWWw7hpHXmQMcshZkqWsbc8TWbIIEL2PO8K5cccFn7UKOgUASxQcFIgruwJqnv8Wi96IfngHm72Z7byNNtckFUrnIdbb5SDgMLEAIHSeh6L4Je5YxcB4L43t3oSwovMoeZ0S5wxwguhCbmoCSqhM5RxOqUHT4Py2k3ZVqgiV7PXTAcL0hPHoOXhjSPZJx.ZN4cn2k0o86QHZH7E0E3zVoJkqopVtVwhUzKWsdQhGj3v4yX0EM2vrqUTrCr0pTqrN.CpUpBrldHE0F4xd93e9gU0T8xv7nnURujdEhFS50sbzbnnM5uDCAwtlrzAwXF8dJWZJHrLkJ2D98NQLLGHJJvCGrjyjKGNcwYJdECgCj0lBxtSWTN7JOO9EtCU9bJHvOcQZlap72Dd.M7al0Jmdyr959FiAe4L+mA93PQUwGiLtsoUYGccYOzQqZ1d29Aas4vw+lfSoTPOEghBAYlJMoYFZ8FcI4G.HXFtJaA1SLj5UcZcTko+X.yx2Y46Ddl1nDREwt9wVs9Jxm.F4geg.lm1DJPHFlmJQbBVhHwLCOK1QExE5epyJS5MnKT6wHyUGKZoebfVhkgeCmukWG1V6Zzx0YWxNd3B5bn2.1Tzib.Ka.eVQS547+KDgD7eJYr1sUKdPEaf5i6.KNtCrz3Nvxi6.qLtCr53NvZidfa.dlnjCJqCTDr4p3ul4YnJJ.NtH8+O8qUVK
      

      Example

      mod.gif

      Overview

      This script implements a flexible, modular FX interface that allows you to:

      • Drag and drop effect panels to rearrange them
      • Automatically reassign slots when effects are moved
      • Save and recall effect order in presets
      • Enable/disable effects individually
      • Use any HISE component (floating tile analyser, knobs, buttons, etc.)

      How It Works:

      The system uses a data-based approach where effects are defined in a central EFFECTS array. Each panel represents a single effect and is linked to a dedicated FX slot. When panels are dragged and dropped, the system detects the panel's new position in the grid, updates its value to reflect this position. It then reconnects the panel and components to the appropriate slot.

      Adding a New Effect

      To add a new effect to the system (for example, a "Chorus" effect):

      1. Create UI Components in the Interface Designer
      • Panel: Create a panel with ID pnl_Chorus
      • Knobs: Create knobs with prefix knb_Chorus (e.g., knb_ChorusMix, knb_ChorusRate)
      • Enable Button: Create a button with ID btn_EnableChorus
      • Analyzer (optional): If your effect needs an analyzer, create it with ID ft_ChorusDisplay
      1. Add your new effect to the EFFECTS array in the script:
      const var EFFECTS = [
          // ... existing effects ...
          {id: "Chorus", displayName: "Chorus", knobPrefix: "knb_Chorus", hasAnalyzer: true}
      ];
      

      The analyzers are really just there to show you how to add something that isn't a knob...

      1. Create a Knob Handler Function
        Add a new handler function for your effect's knobs:
      inline function onChorusKnob(component, value)
      {
          local index = knobGroups[6].indexOf(component); // Use the correct index
          if (index != -1)
              fxSlotRefs[6].setAttribute(index, value);
      }
      
      1. Add the Handler to the knobHandlers Array
      const var knobHandlers = [
          // ... existing handlers ...
          onChorusKnob
      ];
      
      1. Create the FX Chain in HISE
        Make sure to create an actual FX slot in HISE's processor chain with the new effect type.
      posted in Snippet Waiting Room
      HISEnbergH
      HISEnberg

    Latest posts made by HISEnberg

    • The Audio Market Over the Next 10 Years

      I wouldn't usually post something like this but I looked into some recent (2023 & 2025) market research reports and thought I would share some encouraging news. According to these sources, the market for audio DSP is projected to double over the next decade. Obviously AI is a big reason behind this but interestingly immersive technologies and voice assistants take a big share of the market as well (as does DSP).

      For anyone who’s already invested years into this field, or for those considering getting into the game, I hope this can add a little enthusiasm and hope!

      Some highlights:

      Market growth:
      The global market for AI audio processing software is projected to grow from $3.8B in 2023 to over $18B by 2033 (CAGR ~16.8%).

      DSP software expansion:
      The broader audio DSP market is expected to nearly double, from around $6.2B in 2025 to $12.4B by 2034 (CAGR ~8.1%).

      Link Preview Image
      AI Audio Processing Software Market

      AI Audio Processing Software Market is estimated to reach USD 18 billion By 2033, Riding on a Strong 16.8% CAGR during forecast period.

      favicon

      Market.us (market.us)

      Link Preview Image
      Audio DSP Market Size, Share, Growth and Drivers 2034.

      Audio DSP Market is projected to grow from USD 6.17 Bn in 2025 to USD 12.36 Bn by 2034, exhibiting a CAGR of 8.12% during the forecast period 2025 - 2034.

      favicon

      (www.marketresearchfuture.com)

      posted in Blog Entries
      HISEnbergH
      HISEnberg
    • RE: Exploring GPT Integration for Parameter Control in HISE?

      @marcrex I've not done this in HISE but I've done this in JUCE: A text-to-impulse response generator. Users could input a short text (a big empty chamber), and a server-side model (I used TangoFlux) generated the IR audio file. I used a tagging system to structure the prompts and a local GUI that sends HTTP requests to the model running on a GPU backend (so like what David mentions).

      In short it could definitely be done but you would really need to narrow down specifically what you are trying to accomplish. If you're trying to generate audio from text (e.g., new samples or effects), you’ll likely need to offload that to a server. Most users won’t have the GPU horsepower for real-time generation locally, especially with modern diffusion models.

      If you want to use GPT to interpret text and adjust plugin parameters (The user says "create a metallic tap", GPT understands: lower the filter cutoff, shorten the envelope shape, etc.) that's a little easier. You could probably get GPT to map descriptions to internal parameters using a set of structured rules or fine-tuned examples (so give it a structured breakdown of your parameters and guidelines on mapping them).

      posted in General Questions
      HISEnbergH
      HISEnberg
    • Filter Display in External C++ Node

      Just carrying on the discussion from here and creating a short primer on implementing the Filter Display in an external C++ node.

      Disclaimer: I am not very experienced with C++ so there is potentially some/a lot of flaws with my implementation (open to suggestions as always).

      Implementing it was actually a lot more straightforward than I anticipated. My example is just using a direct form biquad since that is what I was working on at the time. This should hypothetically work for any other IIR Filter.

      I think for your own custom filters it's a bit more time consuming as you would need to approximate the coefficients to the ones found in JUCE IIRFilter class (more information about them is here & here)

      g.gif

      Step 1: Change Base Class Inheritance

      Inherit from the filter_base class

      template <int NV> struct MyFilter : public data::filter_base
      

      Reference: FilterNode

      Step 2: Enable Filter Display

      static constexpr int NumFilters = 1;

      Step 3: Store the Filter Coefficients

      IIRCoefficients currentCoefficients;

      Step 4: Add HISE Coefficient Data

      FilterDataObject::CoefficientData coefficientData;

      Step 5: Override setExternalData
      		void setExternalData(const ExternalData& data, int index) override
      		{
      			if (index == 0) 
      			{
      				filter_base::setExternalData(data, index);
      			}
      		}
      
      Step 6: Initial Display Notification Method
      void prepare(PrepareSpecs specs)
      {
          // ... your code ...
          
          this->sendCoefficientUpdateMessage(); // Notify HISE display system
      }
      

      Reference: sendCoefficientUpdateMessage()

      Step 7: Implement getApproximateCoefficients
      FilterDataObject::CoefficientData getApproximateCoefficients() const override
      {
          return coefficientData;
      }
      

      This is the key method HISE calls to get coefficients for display.

      Step 8: Parameter Change Notification
      template <int P> void setParameter(double v)
      {
          bool needsUpdate = false;
          
          // ... check if parameters changed ...
          
          if (needsUpdate)
          {
              calculateCoefficients();
              this->sendCoefficientUpdateMessage();Notify display system
          }
      }
      
      Step 9: Update Coefficient Data
      void calculateCoefficients()
      {
          // Calculate your filter coefficients (example using JUCE)
          switch (filterType)
          {
              case 0:
                  currentCoefficients = IIRCoefficients::makeLowPass(sampleRate, frequency, q);
                  break;
              // ... more filter types ...
          }
          
          // Update HISE display data
          coefficientData.first = currentCoefficients;
          coefficientData.second = 1; 
          coefficientData.obj = nullptr;
          coefficientData.customFunction = nullptr; // Use default HISE display function
      }
      

      Full code example:

      #pragma once
      #include <JuceHeader.h>
      
      /*
      ==========================| HISE Filter Display Integration Guide |==========================
      This demonstrates the COMPLETE process for adding filter display to any HISE node
      
      STEP 1: Inherit from data::filter_base
         - Change: public data::base → public data::filter_base
      
      STEP 2: Enable filter display
         - Set: static constexpr int NumFilters = 1;
      
      STEP 3: Add coefficient storage
         - Add: IIRCoefficients currentCoefficients;
      
      STEP 4: Add HISE coefficient data
         - Add: FilterDataObject::CoefficientData coefficientData;
      
      STEP 5: Override setExternalData
         - Add: void setExternalData(const ExternalData& data, int index) override
      
      STEP 6: Initial display notification
         - Add: this->sendCoefficientUpdateMessage(); in prepare()
      
      STEP 7: Implement getApproximateCoefficients
         - Add: FilterDataObject::CoefficientData getApproximateCoefficients() const override
      
      STEP 8: Parameter change notification
         - Add: this->sendCoefficientUpdateMessage(); when parameters change
      
      STEP 9: Update coefficient data
         - Set: coefficientData.first = currentCoefficients; when coefficients change
      
      */
      
      namespace project
      {
      	using namespace juce;
      	using namespace hise;
      	using namespace scriptnode;
      
      	template <int NV> struct FilterDisplay : public data::filter_base  // STEP 1: Inherit from filter_base
      	{
      		// Metadata Definitions ------------------------------------------------------------------------
      
      		SNEX_NODE(FilterDisplay);
      
      		struct MetadataClass
      		{
      			SN_NODE_ID("FilterDisplay");
      		};
      
      		// Node characteristics
      		static constexpr bool isModNode() { return false; };
      		static constexpr bool isPolyphonic() { return NV > 1; };
      		static constexpr bool hasTail() { return false; };
      		static constexpr bool isSuspendedOnSilence() { return true; };
      		static constexpr int getFixChannelAmount() { return 2; };
      
      		// STEP 2: Enable filter display in external data requirements
      		static constexpr int NumTables = 0;
      		static constexpr int NumSliderPacks = 0;
      		static constexpr int NumAudioFiles = 0;
      		static constexpr int NumFilters = 1;
      		static constexpr int NumDisplayBuffers = 0;
      
      		// Filter Parameters ------------------------------------------------------------------------
      		double frequency = 1000.0;
      		double q = 0.707;
      		double gain = 0.0;
      		int filterType = 0;
      		double sampleRate = 44100.0;
      
      		// STEP 3: Use JUCE coefficients for processing AandND display
      		IIRCoefficients currentCoefficients;
      
      		// Biquad state variables for manual processing
      		double x1 = 0.0, x2 = 0.0, y1 = 0.0, y2 = 0.0;     // Left channel
      		double x1r = 0.0, x2r = 0.0, y1r = 0.0, y2r = 0.0; // Right channel
      
      		// STEP 4: Filter coefficient data for HISE display system
      		FilterDataObject::CoefficientData coefficientData;
      
      		// Callbacks ------------------------------------------------------------------------
      
      		void prepare(PrepareSpecs specs)
      		{
      			sampleRate = specs.sampleRate;
      			calculateCoefficients();
      			reset();
      
      			// STEP 6: send the coefficient update message to notify HISE display
      			this->sendCoefficientUpdateMessage();
      		}
      
      		void reset() 			// Clear
      		{
      			x1 = x2 = y1 = y2 = 0.0;
      			x1r = x2r = y1r = y2r = 0.0;
      		}
      
      		void handleHiseEvent(HiseEvent& e)
      		{
      			// No MIDI processing
      		}
      
      		template <typename T> void process(T& data)
      		{
      			static constexpr int NumChannels = getFixChannelAmount();
      			auto& fixData = data.template as<ProcessData<NumChannels>>();
      
      			auto fd = fixData.toFrameData();
      
      			while (fd.next())
      			{
      				processFrame(fd.toSpan());
      			}
      		}
      
      		template <typename T> void processFrame(T& data)
      		{
      			// Process using JUCE coefficients
      			// JUCE coefficients format: [b0, b1, b2, a0, a1, a2] where a0 = 1.0
      			auto* coeffs = currentCoefficients.coefficients;
      
      			// Left channel biquad processing
      			double input = data[0];
      			double output = coeffs[0] * input + coeffs[1] * x1 + coeffs[2] * x2
      				- coeffs[4] * y1 - coeffs[5] * y2;
      
      			x2 = x1; x1 = input;
      			y2 = y1; y1 = output;
      
      			data[0] = static_cast<float>(output);
      
      			// Right channel (if stereo)
      			if (getFixChannelAmount() > 1)
      			{
      				double inputR = data[1];
      				double outputR = coeffs[0] * inputR + coeffs[1] * x1r + coeffs[2] * x2r
      					- coeffs[4] * y1r - coeffs[5] * y2r;
      
      				x2r = x1r; x1r = inputR;
      				y2r = y1r; y1r = outputR;
      
      				data[1] = static_cast<float>(outputR);
      			}
      		}
      
      		int handleModulation(double& value)
      		{
      			return 0;
      		}
      
      		// STEP 5: Override setExternalData for filter base class
      		void setExternalData(const ExternalData& data, int index) override
      		{
      			if (index == 0) 
      			{
      				filter_base::setExternalData(data, index);
      			}
      		}
      
      		// STEP 7: Implement getApproximateCoefficients for HISE display
      		FilterDataObject::CoefficientData getApproximateCoefficients() const override
      		{
      			return coefficientData;
      		}
      
      		// Parameter Functions -------------------------------------------------------------------------
      
      		template <int P> void setParameter(double v)
      		{
      			bool needsUpdate = false;
      
      			if (P == 0) // Frequency
      			{
      				if (frequency != v)
      				{
      					frequency = jlimit(20.0, 20000.0, v);
      					needsUpdate = true;
      				}
      			}
      			else if (P == 1) // Q Factor
      			{
      				if (q != v)
      				{
      					q = jlimit(0.1, 30.0, v);
      					needsUpdate = true;
      				}
      			}
      			else if (P == 2) // Gain 
      			{
      				if (gain != v)
      				{
      					gain = jlimit(-24.0, 24.0, v);
      					needsUpdate = true;
      				}
      			}
      			else if (P == 3) // Filter Type
      			{
      				int newType = jlimit(0, 7, (int)v);
      				if (filterType != newType)
      				{
      					filterType = newType;
      					needsUpdate = true;
      				}
      			}
      
      			// STEP 8: Update coefficients and notify display when parameters change
      			if (needsUpdate)
      			{
      				calculateCoefficients();
      				this->sendCoefficientUpdateMessage(); // Notify HISE display system
      			}
      		}
      
      		void createParameters(ParameterDataList& data)
      		{
      			// Frequency 
      			{
      				parameter::data p("Frequency", { 20.0, 20000.0 });
      				registerCallback<0>(p);
      				p.setDefaultValue(1000.0);
      				p.setSkewForCentre(1000.0); 
      				data.add(std::move(p));
      			}
      
      			// Q parameter 
      			{
      				parameter::data p("Q", { 0.1, 10.0 });
      				registerCallback<1>(p);
      				p.setDefaultValue(0.707);
      				p.setSkewForCentre(1.0);
      				data.add(std::move(p));
      			}
      
      			// Gain
      			{
      				parameter::data p("Gain", { -24.0, 24.0 });
      				registerCallback<2>(p);
      				p.setDefaultValue(0.0);
      				data.add(std::move(p));
      			}
      
      			// Filter Type
      			{
      				parameter::data p("FilterType", { 0.0, 7.0, 1.0 });
      				registerCallback<3>(p);
      				p.setDefaultValue(0.0);
      
      				StringArray filterNames;
      				filterNames.add("LowPass");   
      				filterNames.add("HighPass");   
      				filterNames.add("BandPass");  
      				filterNames.add("Notch");    
      				filterNames.add("AllPass");   
      				filterNames.add("LowShelf");   
      				filterNames.add("HighShelf");  
      				filterNames.add("Peak");      
      				p.setParameterValueNames(filterNames);
      				data.add(std::move(p));
      			}
      		}
      
      	private:
      		// Helper Functions ----------------------------------------------------------------------------
      
      		void calculateCoefficients()
      		{
      			// Use JUCE's proven coefficient calculation
      			switch (filterType)
      			{
      			case 0: // LowPass
      				currentCoefficients = IIRCoefficients::makeLowPass(sampleRate, frequency, q);
      				break;
      
      			case 1: // HighPass
      				currentCoefficients = IIRCoefficients::makeHighPass(sampleRate, frequency, q);
      				break;
      
      			case 2: // BandPass
      				currentCoefficients = IIRCoefficients::makeBandPass(sampleRate, frequency, q);
      				break;
      
      			case 3: // Notch
      				currentCoefficients = IIRCoefficients::makeNotchFilter(sampleRate, frequency, q);
      				break;
      
      			case 4: // AllPass
      				currentCoefficients = IIRCoefficients::makeAllPass(sampleRate, frequency, q);
      				break;
      
      			case 5: // LowShelf
      				currentCoefficients = IIRCoefficients::makeLowShelf(sampleRate, frequency, q,
      					Decibels::decibelsToGain(gain));
      				break;
      
      			case 6: // HighShelf
      				currentCoefficients = IIRCoefficients::makeHighShelf(sampleRate, frequency, q,
      					Decibels::decibelsToGain(gain));
      				break;
      
      			case 7: // Peak
      				currentCoefficients = IIRCoefficients::makePeakFilter(sampleRate, frequency, q,
      					Decibels::decibelsToGain(gain));
      				break;
      
      			default:
      				currentCoefficients = IIRCoefficients::makeLowPass(sampleRate, frequency, q);
      				break;
      			}
      
      			// STEP 9: Update coefficient data for HISE display
      			coefficientData.first = currentCoefficients;
      			coefficientData.second = 1; 
      			coefficientData.obj = nullptr;
      			coefficientData.customFunction = nullptr; 
      		}
      	};
      
      }
      
      posted in C++ Development
      HISEnbergH
      HISEnberg
    • RE: [Free Dsp] Analog Filter (24dB/oct)

      @Orvillain Ah yes I meant to revisit that. I did figure it out but the solution was admittedly more AI-influenced than I care to admit, so I was waiting until I have a more proper understanding of it to share.

      posted in ScriptNode
      HISEnbergH
      HISEnberg
    • RE: [Free Dsp] Analog Filter (24dB/oct)

      @griffinboy Ya I haven't tried either, I was just be lazy and reaching out for a solution first before trying it myself. I'll look through some of the stock HISE filters and post here once I find the solution.

      posted in ScriptNode
      HISEnbergH
      HISEnberg
    • RE: [Free Dsp] Analog Filter (24dB/oct)

      @griffinboy Just curious, have you figured out how to incorporate the filter display in external C++ nodes?

      posted in ScriptNode
      HISEnbergH
      HISEnberg
    • RE: Is this what I think it is? :D

      @Lindon Yes I recall debating this a few months ago in one of the HISE meetings. You're essentially right as it looks like the dynamic container is essentially doing this anyways on the backend, just dumping the data to JSON (and writeToStream which is a new one I am not familiar with).

      Probably it boils down to preference, I like having as few UI components doing the heavy lifting with scripting.

      posted in General Questions
      HISEnbergH
      HISEnberg
    • RE: Is this what I think it is? :D

      @Chazrox Great question!

      Essentially it means being able to add, remove or modify a component (knobs, buttons, etc.) to the user interface face after it has been initialized (runtime modification). Usually when you ship your plugin the knobs, components are static, they remain in the same place and do the same thing at all times. The dynamic container (assuming I understand it correctly), would allow you to add in or remove components to the UI even after the plugin is initialized. The only component that can do something like this is the ScriptPanel, so the dynamic container will extend this behaviour to all the other component.

      A primary use case for this would be a modular FX system (reordering of the FX chain). I've posted about this several times and why it is so challenging/annoying to setup. Say you have 4 slots, with 4 different effects (compressor, reverb, filter, delay). You either have to preallocate a bunch of UI components for the different FX slots (so create 4 compressors, 4 reverbs, etc. for each slot), or just use the scriptPanel and redraw everything so you can move it around.

      The Dynamic Container extends this runtime flexibility to all standard HISE components. So instead of having to pre-create 4 empty effect slots that sit there unused, you can now add and remove knobs, buttons, etc. on the fly after the plugin is already running.

      In short it doesn't necessarily solve any issue that you couldn't already resolve but is a real time saver!

      posted in General Questions
      HISEnbergH
      HISEnberg
    • Is this what I think it is? :D

      Are we finally going to be able to construct/deconstruct any UI component dynamically that is in this container?

      https://github.com/christophhart/HISE/commit/268a66dacb822c88bc8e3c1152aed609bff0542bScreenshot 2025-07-01 at 3.21.15 PM.png

      posted in General Questions
      HISEnbergH
      HISEnberg
    • RE: Windows codesigning certificate - Any suggestions?

      @dannytaurus Yes I have been waiting for Azure to open up for some time now. It looks like the best solution for price point for sure. Like @d-healey mentioned, it's only available in Canada & the USA, which applies to me, but you have to have a registered business that is at least 3 years old, which disqualifies me (and they are quite adamant about this, I already got rejected).

      After what @Lindon mentioned I opened a support ticket and they literally called me as I was writing this. They told me it was possible to open without a business, but I think the customer support has no idea what they are talking about. I'll try and keep this thread alive if I have any success.

      I'm gonna probably pull the trigger on an IV Code-Signing in the meantime and narrowed it down to SSL.com: $129/year (it's the cheapest and is cloud-based, no dongle).

      @jeffd in short no, the standard certificate (IV Code signing) won't get rid of the warning, but should stop antivirus software and microsoft from interrupting the install process. If you have this certificate and your plugin has enough downloads, the virus warning will eventually go away.

      I believe the EV certificate will remove the warning altogether, but you need a registered business and a willingness to say goodby to a few hundred dollars a year.

      Interesting warning you posted though, it sounds like "All code signing certificates (EV and non-EV) will be treated equally" if they meet the Microsoft Standards. The language is very particular and obscure.

      posted in General Questions
      HISEnbergH
      HISEnberg