HISE Logo Forum
    • Categories
    • Register
    • Login
    1. HISE
    2. HISEnberg
    3. Posts
    • Profile
    • Following 0
    • Followers 6
    • Topics 63
    • Posts 702
    • Groups 1

    Posts

    Recent Best Controversial
    • RE: Best Practice for Managing Dynamic FX Chains with UI Buttons?

      @jhonnmick Lindon is right, you will want to spend most of your time looking at the SlotFx API.

      I've shared one template on how you might handle this here:

      Link Preview Image
      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 ...

      favicon

      Forum (forum.hise.audio)

      it's fairly recent but may be outdated now, as the most recent update of HISE contains a dynamic container which is well suited to this task. If you pull from the recent developer branch on github you can experiment with this (I haven't yet).

      posted in General Questions
      HISEnbergH
      HISEnberg
    • RE: MIDI Player overlay panel LAF

      @rzrsharpeprod said in MIDI Player overlay panel LAF:

      when I use my transpose script

      How are you transposing the notes? If you are using the transpose module it should be automatically get placed after the MIDI Player. Are you using a custom script processor?
      I'm not able to recreate the behaviour you described so if you shared a snippet it might help.

      If you minimize the range of the min and max Note, I think this should give you the behaviour you want, so change:

      var minNote = 127;
      var maxNote = 0;
      

      You can also edit this line to simply draw the note’s height in a fixed range (instead of dynamically resizing it

      var noteRect = [xPos, yPos, width, noteHeight];

      posted in General Questions
      HISEnbergH
      HISEnberg
    • RE: MIDI Player overlay panel LAF

      Here you go, this shows how to display the MIDI data in a panel. I think there is also an example in the HISE docs under MIDI somewhere. My version tries to scale the MIDI data to fill the whole panel, so it should resize the notes depending on how far they are from each other (in pitch, not time) in the MIDI sequence. But this will give you full customization over how the notes are displayed using the paint routine.

      HiseSnippet 2161.3ocyXr1Siqb0AVu8RZ2pck5O.28CW4rKXrSBPPnaK4II.IjMIvBrZ6dGrmDOA+B6IgXp1+a89s9mX+OTU0uVs8L1N1NA1GBcuR0BgxbdMmWy4bloqqsJ1yy1kKS1A9NXtL+A999VT8p5HhEWqZbYdFuIQiTi34Xf74p36f77vZbYxr5ALRxr1S3B99W+0JHCjkJNADG2Y1DU7wDSBMAZ28OhXXz.ogGPLSQcw8aoZaU01vdBnNqxKy4fTuFMB2AwHaEdtlHOctLuheq7ETKpsiV97as61pnsw6NDMbnRAshE2tztJE1EUrz1X4s3x7z5ZDpsaeJhh83x7jJ1Z980su0JbCNi3Qtx.yVnv0G14PvMrMzXlHCJWUchgV249IONPJcS7ZqF509S7sAmTL7Du2yCPHjvQZGXlUVT8VcA0S4KodOfJsRJU52MOPFnRPPC6FnJ+Q91sp0RHDhxBJR0IttXKZe7MSvKFA2OB0.WHVjNXerssScKDndZoAyj9U.o8cvKhHyp+JYqbLypAw.GX2yW.TyrQHIoFhh3x7K7JEkkrFr8QkpdlgzfxkkZN3MC1or6A8lluzoiJciZgdFyTFapqzSIemJ96RuZ5VH7YCqV6ldkjNpvssJ29jQUuzsf9M61svwimT.4zz8rxkt8xV0FI207jy6ocadK+iOt01GNQu9QmJcv0GUs.tu6fxGWVhVc312VrRkgRim52s7j1Tc+yaWpfhtdiYRJCqTS+zRSd6fZ05LE0ubkaLeaeyR9ac4EmvwrpvLeS9MO0C65sI10B6Q2rF16ZpsylkmnQrE5iLc.mwlsa2ci5P.0hRPFdavbLazEhDaHmeiZtSLgETJHAfRHMHj71cyuQcMy7anjuzUNlRvIcHcfcrrOYjEhNwE19m1YhYED35iilOe+N1lD.OKcKAXMr0CAlko.QcWZ5BCgoOKjgL.a5Xm.3m2m6KEb+O7J6TJJ3pzn7fAPv0+1K6VwpUOIxXylG1cbkZijp8lxUtqRCpaO8qqn16v1EGewEiLO+5lXkxyFbxzCnie8YM145hnS8OeZs7mK4TW8L+AdzqTPZZmMXX4AjyObl132bwzxM0O4rCNxWt904ct4z563Qk6T+5S6d9AUbtRRZx3Vuw6tdac2wutSy23IctTEqwC0mpcEA45WuUKKsNRSpNEO97AVGHeSsi6fGnXHe4cVGRt7Hc8iv488l42bxc8lVR+j7EOb3NSu9tNct6vCqlJev92z7glDml1NE1nTg++Ii3u8ea7uW4eLXetTk4dRXYtWv2W0k3PSvvRU987sr.iaHBJlktPWHsbqbwZ7UsAJrnRlnqwMbgEwbHVPVdcg7xx41KaVnkjGUXJxUf4lhJdJ7SBAcIkFgoIUYEeY5BruD3NgYGKiOzNoOJHf46OHhp1fcZAKDe4RzwDR1kfI4gocg9Kzd1SnDKr3vIVpThsk3nbY+6YEfuQRCgVrkMLDo5DO1VH9xqFE1Z8k4.YxHJ3eatoPeUjAVfpiCrPAM3Hl.0VfIg.nNHKrQ.wLCwxlBMzAq5mR6PXaQG.SOrJEYMxHfDw2AtQ3u45vaIZTcwbI.ZhIizoh4dejFwjOjtvDDHdk76jBLZVDX4E09SrL7EbXAerVnNSFxTaWr.wKwhBHGvH9mSq0Du5lNTewbB+3OFaYRFXqQTcg+hfbt.1B8orug1tBhL5DHVwzmKFcBgo8VvgkqvtflyV7Nk2uWjxO+KwhaifTJXoXDn0SwejKJloX+QHSnYhQf9BL8w3eE+Cv6UEYnNw.5HKnEkX5fbghLvIAurKaH8fH6B6nx5w5wFyMiTaYPdOK4ILNCbtbfeQZQSQDC1DE8gA+XaTZl2LzwGpCuVPYIdY3h2lXErz5KIzTbk1MTyEcafH7dbgZPDAdg3Lszp1rt1dyi9xueu6g21kLBpQZzILrkJQ4djdK6DzbBxuDAKqQ8vlHmfSvWH3X6QXkHBOXSCfNOh6xboOPlqqIxfbGVKT8EWVMSh3PrINzbec1et4mVduRPLcvciTwubeEqJNSDJy.x7cLW65A6v5g9l0SInGv+4oizruMM2rToPID9qukTV1IGm4r.hQrZzgkaSUAlPwlKUCNgAVoKnftEbwi3hnhysUnajjbtuSMQ.ZKX.8E9hZj7rxkaD7kaOgknRCjxWQMTjjC928Jr7wEJJGnKNQWEHI4iXoQTYCCDWVeNMg4HK0PoaBx.9gxzuZ4lI6k86wemO1gGZgGyZalZuWWXgExRx2uI07XvGYsjSqoPSdKv4LvtKKiVbot0fKlUmMjF1wuv1ooE.n68vNrN5mXM2VgaPBmlDotSvgRHhBgfMIa1487ErCNCdB3bfd+qkEhCKiZ3vGDGa9CWaCCXtkGBMajO2uFihVAsXVGhiFfRNmPXvrEun2S+9tnmZ33PoHz1pkEgdhCNZ8W+V4bQySA+5zVAWS.l2KBFPmC1kRXpSlZ3oDUb3viqwGM6b.sQCgwkIKM.6ylOZIykywFD9E7KEc4lkdDV+zKBpjvkgOy5Y33zCRhXq9m.p4ihAj+zrYy9YF4IIqLne5Se5WVDZdFXNtO+QFXJdFMg3OCegurx8lN947ss0XM3W7MIXubSDBHltvDxrofsfbP+Et792+CUH+0u792oJ9B9tDpp9Cqiq7.5H6s.9MPGiddmmwWe3P37ahB9D9Fm+XeKmuw1GNV+HXHFWBjdwCMa6CQZULql.jI5wRiVgkwGtVlsl4A5iszBVvRHhPpvVmIBoxbjblHUW6OnFdRl8PJ+P.DPmrBth4Z7sYqET3BNcm1OydMvOnptnntGi4erLV3wxXwGKia8XYb6GKi67XYrz2lQVwoxSn1lgGa33Z2sd3qljI945xrJ2+CqyWNGC
      
      Content.makeFrontInterface(300, 200);
      
      const var MIDIPlayer1 = Synth.getMidiPlayer("MIDI Player1");
      const var pnl_MidiDisplay = Content.getComponent("pnl_MidiDisplay");
      
      pnl_MidiDisplay.setPaintRoutine(function(g)
      {
          g.fillAll(this.get("bgColour"));
          
          // Scale the MIDI data to fill the panel
          var noteList = MIDIPlayer1.getNoteRectangleList([0, 0, this.getWidth(), this.getHeight()]);
          var minNote = 127;
          var maxNote = 0;
          
          // Only proceed fill if there is MIDI data
          if (!MIDIPlayer1.isEmpty() && noteList.length > 0)
          {
              for (note in noteList)
              {
                  var noteNumber = note[1];  
                  minNote = Math.min(minNote, noteNumber);
                  maxNote = Math.max(maxNote, noteNumber);
              }
              
              // Calculate display parameters
              var noteRange = Math.max(1, maxNote - minNote);
              var panelHeight = this.getHeight();
              var availableSpace = panelHeight / (noteRange + 1);
              var noteHeight = Math.max(8, availableSpace);
              
              // Draw notes
              for (note in noteList)
              {
                  // Note data
                  var xPos = note[0];
                  var originalNoteNum = note[1];
                  var width = note[2];
                  
                  // Remap the Y position to fit the display range
                  var normalizedPos = (originalNoteNum - minNote) / noteRange;
                  var yPos = normalizedPos * (panelHeight - noteHeight);
                  
                  var noteRect = [xPos, yPos, width, noteHeight];
                  var shadowRect = [xPos + 1, yPos + 1, width, noteHeight];
                  
                  // Draw note
                  g.setColour(this.get("itemColour"));
                  g.fillRoundedRectangle(noteRect, 2.0);
                  
                  // Draw note outline
                  g.setColour(0xAAFFFFFF); 
                  g.drawRoundedRectangle(noteRect, 1.0, 1.0);
              }
          }
          
          // Draw playback position indicator
          var playbackPos = MIDIPlayer1.getPlaybackPosition() * this.getWidth();
          g.setColour(this.get("itemColour2"));
          g.drawLine(playbackPos, playbackPos, 0.0, this.getHeight(), 2.0);
      });
      
      MIDIPlayer1.connectToPanel(pnl_MidiDisplay); // Connect to panel
      MIDIPlayer1.setRepaintOnPositionChange(true); // Repaint Panel
      
      
      

      giffy.gif

      posted in General Questions
      HISEnbergH
      HISEnberg
    • RE: MIDI Player overlay panel LAF

      @rzrsharpeprod Yes you have to display them in a panel and use a paint routine. I'll share a script for this shortly!

      posted in General Questions
      HISEnbergH
      HISEnberg
    • RE: Server.callWithPost content type

      @oskarsh @hisefilo

      I figured out a way to solve this (sort of, a better method would be a new scripting call for the Server API, something like Server.callWithPostRawJSON).

      Most of where you want to look is the GlobalServer.cpp file here

      If you are looking for a quick fix you can try editing line 273 of the GlobalServer.cpp script:

      for(const auto& v: d->getProperties())
      			isComplexObject |= v.value.isArray() || v.value.getDynamicObject() != nullptr;
      
      		// if(isComplexObject) remove this line
              if (true) // add this line
      		{
      			extraHeader = "Content-Type: application/json";
      			url = url.withPOSTData(JSON::toString(parameters, true));
      		}
      		else
      		{
      			for (auto& p : d->getProperties())
      				url = url.withParameter(p.name.toString(), p.value.toString());
      

      I think what happens is that when your JSON objects contain only simple properties (strings, numbers, booleans), HISE will treat it as a simple object and URL-encodes the parameters. So modifying the isComplexObject will just push the JSON data for all objects now (which could cause more issues down the line).

      I'll post here again once I figure out a better/more robust solution.

      posted in General Questions
      HISEnbergH
      HISEnberg
    • RE: Waterfall Spectrum Display

      @oskarsh Just curious did you ever solve your UI lag problem regarding this (we talked about it a few months back)?

      Looks fantastic btw 👀

      posted in General Questions
      HISEnbergH
      HISEnberg
    • RE: Can't figure out how to compile project/export as standalone plugin

      @Bicrome Oh nice that video is much more up to date so it's probably better to follow that one. Your project is still trying to link to IPP meaning 1 of 2 things:

      • IPP is still enabled. After you disabled it you recompiled HISE correct?

      • You're working with an old binary that's linked to IPP still. In your project go to Export > Clean build directory. This will clear out your build folder and you can try building your plugin afresh.

      I think try the second option first.

      posted in Newbie League
      HISEnbergH
      HISEnberg
    • RE: Can't figure out how to compile project/export as standalone plugin

      @Bicrome Ah I see that you also have IPP enabled. Did you download the library for it (I think it's in David's video on how to do this). I believe the recent develop branch has also had problems with IPP lately. You really only need IPP for some niche use-cases so you can also disable it before compiling HISE (There is a flag in the Projucer you can set for this).

      Link Preview Image
      Roadmap to HISE 5

      @Lindon said in Roadmap to HISE 5: what about building without IPP on Windows??? Set this to No

      favicon

      Forum (forum.hise.audio)

      posted in Newbie League
      HISEnbergH
      HISEnberg
    • RE: Can't figure out how to compile project/export as standalone plugin

      @Bicrome Have you set your HISE path to point towards HISE in your project settings?

      Screenshot 2025-07-11 at 12.42.09 PM.png

      posted in Newbie League
      HISEnbergH
      HISEnberg
    • RE: Vst compile error

      @Oriah-Beats There isn't too much information to go off of here. Just a tip you can just copy the compiler's output and paste it within the code brackets using the symbol at the top here:

      Screenshot 2025-07-10 at 1.50.48 PM.png

      It just makes it a bit easier to read.

      Are you able to compile any HISE projects? It would be best to try first with just a test project then you can deduce if the issue is with your HISE setup or with your project.

      posted in General Questions
      HISEnbergH
      HISEnberg
    • RE: function not found "ScriptFX.setParameter("valueknob", value);"

      @el_lauta setParameter doesn't exist, where did you get that from?

      Usually HISE will offer autocomplete or you can search on the lefthand side under API for the different functions related to a specific API.

      So in this case you have ScriptFX = Synth.getEffect("Script FX"); This means you are referencing it as an Effect. If you search "Effect" in the API browser you will get all the functions related to it. In your case you want to try ScriptFX.setAttribute(int parameterIndex, float newValue)

      The docs are also online here:

      Link Preview Image
      HISE | Scripting | Effect

      The FX script reference

      favicon

      (docs.hise.dev)

      posted in General Questions
      HISEnbergH
      HISEnberg
    • RE: Good Faust Reverb or Chorus

      @resonant I really like vital_rev, you can find all the stock reverbs here:
      https://github.com/grame-cncm/faustlibraries/blob/master/reverbs.lib

      posted in Faust Development
      HISEnbergH
      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