Forum
    • Categories
    • Register
    • Login
    1. Home
    2. griffinboy
    3. Posts
    • Profile
    • Following 9
    • Followers 12
    • Topics 119
    • Posts 1,068
    • Groups 1

    Posts

    Recent Best Controversial
    • RE: [Tutorial] How to Multiband in Scriptnode (without artefacts!)

      @ustk

      Yep! A lot of people get caught out with that.
      Because it actually sounds pretty close to correct, if you use an allpass on the high band in the way that Hise's examples do.

      But rather than getting a notch eaten out of your spectrum, this will cause a little bump instead.

      a3da661a-6e5a-4dca-87e1-c7928cd9edd8-image.png

      Which is subtle but it still exists. It's most noticeable when you put two crossovers near the same frequency, you get a dB or more bump in the spectrum.

      posted in Blog Entries
      griffinboyG
      griffinboy
    • RE: Dsp network wont compile: // <--Changed to more relevant title :)

      @Chazrox

      I belive this is the correct branch of Juce you need for Hise now.
      You need to manually place it in the HiseDevelop juce folder before compiling Hise

      https://github.com/christophhart/JUCE_customized

      As @DanH said, this is the thread to look at:
      https://forum.hise.audio/topic/14184/juce-submodule-psa?_=1780482119841

      posted in Scripting
      griffinboyG
      griffinboy
    • RE: [Tutorial] How to Multiband in Scriptnode (without artefacts!)

      @dannytaurus
      I fear I might have explained it badly - only after I started writing this article did I realise how difficult it was to put into words!

      Luckily, Chat GPT exists, and anybody who doesn't have a clue what I'm trying to explain, can hopefully ask GPT to elaborate a bit : )

      posted in Blog Entries
      griffinboyG
      griffinboy
    • [Tutorial] How to Multiband in Scriptnode (without artefacts!)

      I’ve seen a few people try to build multiband chains in ScriptNode and run into the same problem:

      The filters look like they are splitting bands correctly,

      6cbc85b5-008e-40b1-9d56-eb3419dda181-image.png
      (image note: the middle band filters are a highpass node followed by a lowpass node, they share a linked filter display which is why they both look like a bandpass on the display. That's a drawing of the combined response, but it's actually a separate lowpass and highpass node)


      but when you sum them back together the sound gets hollow.
      The spectrum has a dip!

      It is phase cancellation.

      d31af9ae-ca53-4fc8-9e17-9b6b8327fe4a-image.png

      I actually do have my own custom multiband node for this, that handles everything automatically, with higher quality filters that can be modulated without glitches. And I’ll be releasing that to the forum for free in the future.

      But until then:
      It's actually possible to make a basic splitter in Hise using stock effects.
      So without further ado, lets begin : )

      The mistake

      A multiband splitter does not work if you just:

      • lowpass for the low band
      • bandpass for the middle band
      • highpass for the high band
      • then sum everything back together

      That looks logical, but the filters are not only changing volume.

      They are also changing phase.

      So when the bands recombine, your signals are out of phase with each other and don't add together properly. Part of the signal cancels out and you get a frequency dip.

      That is the hole.


      Building a perfect multiband in ScriptNode


      e735ee21-55b7-4a84-9b63-cc03d0947cab-ezgif-141257e24d046bf1.gif

      Use Linkwitz-Riley filters for the crossover split. In HISE, that means jdsp.jlinkwitzriley.

      In Linkwitz-Riley the LP and HP modes are designed to add back together cleanly when they use the same crossover frequency.

      For 2 bands, it's easy.
      For more than 2 bands, you also need AP mode.
      That is where the annoying part begins.


      The simple case: 2 bands

      A 2-band splitter is just one matched crossover.

      02afbf8f-5c8b-44b2-a7a8-3dc1dfe75b07-image.png

      That gives you:

      Low:
      LP 1
      
      High:
      HP 1
      

      Both filters use the same crossover frequency.

      Nice and clean. No phase artefacts yet.


      The hard case: more than 2 bands

      The 2-band split is easy because there is only one crossover.
      (One side gets LP 1, the other side gets HP 1, and they are a matched pair).

      With 3 bands or more, it's different.
      This is where the simple setup breaks.

      Lets use 3 bands as an example.
      With 3 bands there are two crossovers:

      faec4798-a139-4051-8a68-d7413c9f0e36-image.png

      It is tempting to think this is correct:

      Low:
      LP 1
      
      Mid:
      HP 1 -> LP 2
      
      High:
      HP 2
      

      That looks reasonable at first glance.

      Low is lowpassed.
      Mid is between the two crossovers.
      High is highpassed.

      But the paths are not phase equivalent anymore.

      Filters affect phase, so if one band gets shifted differently from the others, the bands will not line up properly when summed:

      In a 3 band multiband split, we have two crossovers.
      The mid band has already gone through two crossover filter stages:

      Mid:
      HP 1 -> LP 2
      

      But the low and high bands have only gone through one crossover filter stage each.
      That is the mismatch.

      The mid band has been shaped by crossover filter 1 and crossover filter 2, so its phase has moved through both filter stages.

      But In the naïve version, the high band is only:

      High:
      HP 2
      

      That skips crossover 1 completely.

      We need both crossovers to be represented.

      So the high band should be:

      High:
      HP 1 -> HP 2
      

      Not because we want to “highpass it twice” for the sound, but because the high band still needs the phase from crossover 1 and well as crossover 2.

      The low band has the opposite problem.

      It has crossover 1 already:

      Low:
      LP 1
      

      But it does not have crossover 2 at all.

      We do not want to filter the low band with LP 2 or HP 2, because that would change the low band itself.

      We only want it to carry the phase movement from crossover 2.

      That is what an allpass is for in this situation:

      Low:
      LP 1 -> AP 2
      

      Now the full 3-band layout is:

      Low:
      LP 1 -> AP 2
      
      Mid:
      HP 1 -> LP 2
      
      High:
      HP 1 -> HP 2
      

      This is the correct layout to copy.

      a4fd7eb8-0227-4640-b851-5e5d7b01fecc-image.png

      The allpass is not changing the frequency balance. It is just giving the low path the same phase movement from crossover 2, so everything adds back together properly.

      No more frequency dip! : )

      895d9d21-f183-4d91-bebc-b6c0bd9e3a01-image.png


      Add your effects after the splitter

      Once the splitter is correct, add whatever effects you wanted to apply to each band, after the crossover nodes.

      4f2f6c13-3f38-4da1-aaa6-42b86812ee36-image.png


      The generic recipe

      Once you understand the 3-band version, the bigger versions are just more of the same.

      The practical way to do this is to work band by band.

      Each band is one branch in your container.split.

      For each band, you need as many jdsp.jlinkwitzriley filters as crossover frequencies.

      So if you want 6 bands, that means you'll have 5 crossovers, and so each branch should have 5 Linkwitz-Riley filters placed inside it.

      The filter modes change depending on which band you are making.

      For Band "X":

      • add HP filters for every crossover before Band "X"
      • add LP for the crossover at the top of Band "X"
      • add AP filters for every crossover above Band "X"

      The top band is the only exception.

      It has no top cutoff, so it is just HP for every crossover.

      Lets do an example with a 6-band splitter.
      A 6 band splitter has 5 crossovers, so needs 5 filters on each band:

      Band 1:
      LP 1 -> AP 2 -> AP 3 -> AP 4 -> AP 5
      
      Band 2:
      HP 1 -> LP 2 -> AP 3 -> AP 4 -> AP 5
      
      Band 3:
      HP 1 -> HP 2 -> LP 3 -> AP 4 -> AP 5
      
      Band 4:
      HP 1 -> HP 2 -> HP 3 -> LP 4 -> AP 5
      
      Band 5:
      HP 1 -> HP 2 -> HP 3 -> HP 4 -> LP 5
      
      Band 6:
      HP 1 -> HP 2 -> HP 3 -> HP 4 -> HP 5
      

      The HP filters get set to the frequency of the band above the lower crossovers.

      The LP filter gets set to the frequency of the top edge of that band.

      The AP filters are only phase compensation for the higher crossovers and so need those frequencies.

      The rule is not hard.
      But the bookkeeping is a bit fiddly.


      One last trap

      A Linkwitz-Riley multiband split does sum back flat, but it is not phase-identical to the untouched dry signal.

      So be careful with global dry/wet mixing:

      untouched dry signal
      +
      recombined multiband signal
      

      That can create a new cancellation problem after you already fixed the splitter.

      For parallel multiband effects, either mix dry/wet inside the bands, or send the dry path through the same crossover phase path.


      And that's it!
      A native ScriptNode multiband splitter, without the giant spectral bite taken out of it.

      Viola : )


      Extra note: HISE actually has some built-in templates for splitting, but they are set up wrong with mistakes, and so they don't sum together properly!

      2ede47c8-58d6-4162-a37d-8a249a996f03-image.png

      6a49b5be-e575-4699-a2b4-03f96533cc47-image.png

      Also, Hise's Linkwitz-Riley filters aren't using modulation safe designs.
      So if you modulate the crossovers you'll get some pretty bad pops and clicks. This isn't due to parameters needing smoothing. This is to do with the way the filters are written internally.

      posted in Blog Entries
      griffinboyG
      griffinboy
    • RE: [Blog] My Favourite C++ Open Source DSP References

      @Orvillain

      Yes, I'm mostly using hardcoded slots myself nowadays rather than scriptnode : )
      There seem to be a few bugs still left with modulation and C++ nodes, but once those are ironed out, this is the way!

      37cf8fbd-fa43-4600-9442-e03516f8e768-image.png

      But I still make scriptnode nodes for other users who are into that workflow.
      And I do think Scriptnode can be good for a fair few things, like when you need a multiband chain:

      40813827-1737-451f-a668-475d67a743f1-image.png

      posted in Blog Entries
      griffinboyG
      griffinboy
    • RE: "Error at node: chain" while 'compiling network to dll'

      @HISEnberg

      Ah missed that! It slipped my mind somehow. thx for mentioning.

      posted in Scripting
      griffinboyG
      griffinboy
    • RE: "Error at node: chain" while 'compiling network to dll'

      @Chazrox

      You're looking for

      Hise Develop

      Snag_a7c86c5.png

      The insides look like this.

      Snag_a7c9684.png

      It's the thing you download / pull from github, and contains all the source for building Hise.

      posted in Scripting
      griffinboyG
      griffinboy
    • RE: "Error at node: chain" while 'compiling network to dll'

      @Chazrox

      Yeah it seems like a deeper issue with your setup.

      If youre not even able to compile simple ones (forget snex, just say, a container and a hise filter) then you've got something wrong with your Hise setup.

      It might be as David says.

      posted in Scripting
      griffinboyG
      griffinboy
    • RE: Custom filter graph output within a custom node?

      @Lindon

      Yeah thats another way to do it!
      Basically the same idea.

      Except like you noted, the Hise filters (which I think are ports of the stock Juce ones?) are primitive, they cramp in the high end of the spectrum (the filter shape get warped near nyquist). And so that's not very nice to see on the graphs.

      Plus, the trouble with analog style filters (if they are actually simulating the hardware topology) is that the cutoff frequency on the knob won't actually line up with the frequency in the real filter, the cutoff frequency on the graph won't really match the real frequency the filter is at internally.

      posted in C++ Development
      griffinboyG
      griffinboy
    • RE: "Error at node: chain" while 'compiling network to dll'

      @Chazrox

      I think it might be a bug, either that or you haven't enabled compilation on the thing you are compiling?

      397b0048-2687-4826-847c-35d0ba88c406-image.png

      This scriftFX chain compiles fine. Its got a container, and a hise filter.
      I haven't tried it with snex, maybe it could even be something specific you've done in your snex node that it doesn't like?

      dba6d83e-3a22-41c7-abbb-0e8050928555-image.png

      posted in Scripting
      griffinboyG
      griffinboy
    • RE: [Blog] My Favourite C++ Open Source DSP References

      @ustk

      Glad it's interesting!
      These are a few very select references.

      But the actual number of codebases and papers I look at when I'm writing DSP is well over 500 that I keep on my harddrive. Obviously it would be silly to paste that many links, and would take too long to explain each one.

      But I can share more of my finds for specific DSP areas if people are interested.

      posted in Blog Entries
      griffinboyG
      griffinboy
    • RE: "Error at node: chain" while 'compiling network to dll'

      @Chazrox

      Could you give more info on what you are trying to compile?

      A scriftFX Chain network?

      Or something like a C++ file?

      Wider screenshot that captures more info please!

      posted in Scripting
      griffinboyG
      griffinboy
    • [Blog] My Favourite C++ Open Source DSP References

      Back From Hibernation: My Favourite C++ Open Source DSP References

      Hi HISE forum!

      It’s been a while since I’ve made a proper post here. I used to be a lot more active, then work swallowed me whole for what feels like a couple of years.

      In that time I’ve mostly been writing C++ DSP, analog modelling, optimisation, and the little details that make audio code really solid.

      1aa92f1b-56a0-4532-a4df-342cad49319f-image.png

      So for my first post in a while, I thought I’d share something useful:
      some of my favourite DSP references.

      These are the papers, codebases and blogs I keep coming back to.

      Not an ultimate list. Just a few of my favourite resources I personally use and think are worth studying if you’re learning C++ DSP.

      disclaimer: This is all intended for intermediate to advanced DSP coders.


      ResearchGate

      ResearchGate

      This is one of the places I use to find research papers. I use other sites as well, but ResearchGate is my first go-to.

      This site has research from lots of different fields, so not everything will be useful for DSP. You’ll have to search.

      If you find a paper you like, you can click the author's profile and see what else they’ve written.

      Good for: finding papers by topic and discovering the best authors working in that research area.


      DAFX Paper Archive

      DAFX paper archive

      The papers are all relevant to DSP.

      DAFX is one of the main places I check when I want to see what is happening on the cutting edge.

      Virtual analog modelling, Neural Effects and AI, reverbs, advanced physical modelling, new optimization methods. That sort of thing.

      Good for: serious audio DSP research and finding techniques you probably won’t see in open source codebases


      Vital

      Vital GitHub

      This is the older open source version of Vital.
      The later versions of Vital are not open source, but the old version is still on GitHub.

      A really nice resource for seeing how a full wavetable synthesiser codebase is organised. The wavetable oscillator engine is worth studying, and the parameter / modulation systems are written with performance in mind.

      The effect DSP is not the best part of the codebase. I would look elsewhere for more rigorous effect implementations.

      Good for: wavetable synthesis, modulation/parameter systems, full plugin architecture.


      chowdsp_utils

      chowdsp_utils GitHub

      This is one of my favourite open source DSP codebases.

      It contains a lot of practical building blocks for audio effects: buffers, filters, delays, math utilities, and lots of other useful pieces.

      The reason I like it so much is that it is clean, modern, and very performance-aware.
      I look at this repo constantly when I want to check how someone else has approached optimizing the essential basics of DSP.

      Good for: highly efficient DSP, modern C++ style.


      chowdsp_wdf

      chowdsp_wdf GitHub

      WDF circuit simulation framework.

      ...You can tell I like Jatin Chowdhury?

      This library is not a complete “simulate any circuit you can imagine” framework. There are many circuits it will not handle. But it's cleanly written and efficient, and it is a great starting point for understanding realtime WDF circuit simulation.

      If you are interested in analog modelling, this is a good repo to study slowly. WDFs can feel pretty alien at first, but seeing a practical implementation helps a lot.

      Good for: learning WDF structure and getting started with realtime circuit modelling.


      Valhalla Reverb Blogs

      Valhalla DSP blog
      Old Valhalla DSP blog
      Reverb Subculture thread on Gearspace

      Getting started with reverb design?

      There are some great snippets on the Valhalla blogs, and a lot of useful information spread across old forum threads if you are patient enough to dig.

      Reverb design is one of those areas where the useful knowledge is scattered across blogs and forums. It's all a bit secretive.

      Good for: algorithmic reverb design, historical context around classic digital reverbs.


      Laurent de Soras

      Laurent de Soras source code

      High performance implementations of FFT, oversampling, resampling, and digital filters.

      This is excellent material for studying a slightly older style of DSP coding.
      The optimisation strategies are different from chowdsp_utils, so it makes a good contrast.

      I like looking at code like this because it reminds you that modern C++ is not the only way to write optimized audio code. Sometimes older DSP code has a directness that is worth studying.

      All of the implementations are efficient, and scientifically rigorous. If I'm not mistaken, this is the man who wrote the antialiasing algorithm for Xfer "Serum" synth.

      Good for: FFT, oversampling, resampling, digital filters


      libsamplerate

      libsamplerate GitHub

      High quality sample rate conversion.

      This is a well regarded implementation if you are interested in resampling quality. The interesting part is the resampling filter design: how the signal is reconstructed when changing sample rate / pitch / playbackspeed, and how the implementation balances quality against cost.

      Resampling is one of those things that looks simple until you try to do it without artefacts.

      Good for: sample rate conversion, resampling filter quality, and understanding aliasing / artefact tradeoffs.


      RipplerX

      RipplerX GitHub

      Basic physical modelling synth.

      It is not fully optimised, but it is a good starting point because the code is easy to read and follow.

      Sometimes that is exactly what you want. A codebase doesn't need to be the most advanced thing on earth to be useful. Sometimes the best learning resource is one where you can clearly see what is happening inside.

      Good for: simple resonator / exciter structures and basic physical modelling before moving on to more optimised implementations.


      How Do I Study These?

      I usually open the example code or research paper on one monitor, then keep ChatGPT or Codex open on the other, and ask it to explain what I’m looking at.

      Repeat until I understand the whole thing.

      While doing this, I write my own code, compare against the example codebases, read related papers, and look at other implementations of the same idea. Yes, this process takes years!

      There isn’t any particular secret, I’m afraid! Just a lot of time and hard work.

      Once you’ve studied enough papers and codebases, you start seeing extra techniques the original author didn’t use, and that’s where you can improve the accuracy or efficiency, and create a piece of DSP that is better than your reference.

      Nowadays I always benchmark my own work against the next best open source implementations. When I write new DSP, I want to make sure it outperforms the best public examples.
      Fun challenge for yourself ; )

      Anyway, that’s enough from me!
      Hopefully this is interesting to someone out there.

      If there is an area of DSP you’re looking into, feel free to send me a message or comment on this thread! I’d be more than happy to share my favourite resources / chat.

      posted in Blog Entries
      griffinboyG
      griffinboy
    • RE: Volume Doesn’t Change

      @justinfitzmusic

      The green marker is just showing you the velocity values the sampler is "receiving"

      It won't actually do anything with that received information, unless you do something like @Lindon said: add a velocity modulator to modulate something!

      For volume, just apply a velocity modulator to gain:
      602bdb89-e71b-4534-841d-4810c02eca1d-image.png

      posted in General Questions
      griffinboyG
      griffinboy
    • RE: Custom filter graph output within a custom node?

      @Orvillain

      Yes that's right.
      By the way it won't work for nonlinear filters, it's designed to get the frequency response from simple analytic (linear) filters. And so if you want to draw any complex filter responses, you'll have to approximate the frequency response using a different cheap filter (see below)

      Also if its a polyphonic filter, you might want to make sure that only one persistent graphing filter instance exists, rather than having the graph be fed by a bank of polyphonic filters all fighting for attention. You can then use the hise voice system to decide which voices control the graph (first voice only, or latest voice only) that kind of thing.

      Here is how I do it currently for my filters, although there is talk of this graphing system being updated for Hise in the future, so this might all change...

      (simplified cut down code excerpt from one of my large experimental filter nodes)

      // ==============================| Griffin Poly EQ Filter |===================================
      //
      //  File:        Griffin_EQFilter_Poly.h
      //  Node:        Griffin_EQFilter_Poly
      //  Package:     Griffin DSP Essentials for HISE
      //  Author:      Griffinboy
      //  HISE Forum:  https://forum.hise.audio/user/griffinboy
      //  Copyright:   Copyright (c) 2026 Griffinboy
      //
      //  Description:
      //      Minimum-phase EQ filter with per-voice state and frame processing.
      //      Designed for sample-accurate modulation in synthesis contexts.
      //      Uses more CPU than the block-rate Griffin_EQFilter.
      //
      //      Has Mod slots for frequency, Q, and gain.
      //
      //  License:
      //      GNU General Public License v3.0 or later (GPL-3.0-or-later).
      //
      //      This file is part of Griffin DSP Essentials for HISE.
      //
      //      This program is free software: you can redistribute it and/or modify it under the
      //      terms of the GNU General Public License as published by the Free Software Foundation,
      //      either version 3 of the License, or (at your option) any later version.
      //
      //      This program is distributed in the hope that it will be useful, but WITHOUT ANY
      //      WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
      //      PARTICULAR PURPOSE. See the GNU General Public License for more details.
      //
      //      You should have received a copy of the GNU General Public License along with this
      //      program. If not, see <https://www.gnu.org/licenses/>.
      //
      // ================================================================================================
      
      // SPDX-License-Identifier: GPL-3.0-or-later
      #pragma once
      #include <algorithm>
      #include <cmath>
      #include <JuceHeader.h>
      #include "src/griffinboy/modules/essentials/eq_filter/eq_filter.h"
      
      namespace project
      {
      using namespace juce;
      using namespace hise;
      using namespace scriptnode;
      
      template <int NV> struct Griffin_EQFilter_Poly: public data::filter_node_base
      {
          using VoiceState = griffin::modules::essentials::eq_filter::EQFilterFrameProcessor;
          using PlotModel = griffin::modules::essentials::eq_filter::EQPlotModel;
          using Defaults = griffin::modules::essentials::eq_filter::EQFilterDefaults;
      
          PolyData<VoiceState, NV> voices;
          PlotModel plotModel;
          SimpleReadWriteLock topologyLock;
          double plotSampleRate = 44100.0;
      
          SNEX_NODE(Griffin_EQFilter_Poly);
      
          struct MetadataClass
          {
              SN_NODE_ID("Griffin_EQFilter_Poly");
          };
      
          static constexpr bool isModNode() { return false; };
          static constexpr bool isPolyphonic() { return NV > 1; };
          static constexpr bool hasTail() { return true; };
          static constexpr bool isSuspendedOnSilence() { return true; };
          static constexpr int getFixChannelAmount() { return 2; };
      
          static constexpr int NumTables = 0;
          static constexpr int NumSliderPacks = 0;
          static constexpr int NumAudioFiles = 0;
          static constexpr int NumFilters = 1;
          static constexpr int NumDisplayBuffers = 0;
      
          void prepare(PrepareSpecs specs)
          {
              SimpleReadWriteLock::ScopedWriteLock sl(topologyLock);
              voices.prepare(specs);
              for (auto& voice : voices)
                  voice.prepare(specs.sampleRate, specs.blockSize);
              plotModel.prepare(specs.sampleRate);
      
              plotSampleRate = specs.sampleRate > 0.0 ? specs.sampleRate : 44100.0;
      
              if (auto fd = dynamic_cast<FilterDataObject*>(this->externalData.obj))
                  fd->setSampleRate(plotSampleRate);
      
              sendCoefficientUpdateMessage();
      
              voiceManager.prepare(specs);
              voiceManager.setActive(1.0);
          }
      
          void reset()
          {
              SimpleReadWriteLock::ScopedWriteLock sl(topologyLock);
      
              for (auto& voice : voices)
                  voice.reset();
      
              voiceManager.reset();
          }
      
          void handleHiseEvent(HiseEvent& e)
          {
              voiceManager.handleHiseEvent(e);
          }
      
          template <typename T> void process(T& data)
          {
              if (auto sl = SimpleReadWriteLock::ScopedTryReadLock(topologyLock))
              {
                  static constexpr int NumChannels = getFixChannelAmount();
                  auto& fixData = data.template as<ProcessData<NumChannels>>();
                  auto& voice = voices.get();
      
                  auto fd = fixData.toFrameData();
                  while (fd.next())
                      voice.processFrame(fd.toSpan());
      
                  voiceManager.process(data);
              }
          }
      
          template <typename T> void processFrame(T& data)
          {
              if (auto sl = SimpleReadWriteLock::ScopedTryReadLock(topologyLock))
                  voices.get().processFrame(data);
          }
      
          int handleModulation(double& value)
          {
              ignoreUnused(value);
              return 0;
          }
      
          double getPlotValue(int getMagnitude, double freqNorm) override
          {
              if (getMagnitude == 0)
                  return 0.0;
      
              const auto frequency = std::clamp(freqNorm, 0.0, 0.5) * plotSampleRate;
              return plotModel.getMagnitudeAtFrequency(frequency);
          }
      
          void setExternalData(const ExternalData& data, int index)
          {
              data::filter_node_base::setExternalData(data, index);
              ignoreUnused(index);
      
              if (auto fd = dynamic_cast<FilterDataObject*>(data.obj))
                  fd->setSampleRate(plotSampleRate);
      
              sendCoefficientUpdateMessage();
          }
      
          void createExternalModulationInfo(OpaqueNode::ModulationProperties& info)
          {
              modulation::ParameterProperties::ConnectionList list;
      
              auto addParameterSlot = [&list](int parameterIndex)
              {
                  modulation::ConnectionInfo slot;
                  slot.connectedParameterIndex = parameterIndex;
                  slot.modColour = HiseModulationColours::ColourId::FX;
                  slot.connectionMode = modulation::ConnectionMode::Parameter;
                  slot.modulationMode = modulation::ParameterMode::ScaleAdd;
                  list.push_back(slot);
              };
      
              addParameterSlot(2);
              addParameterSlot(3);
              addParameterSlot(4);
      
              info.fromConnectionList(list);
              info.setModulationBlockSize(Defaults::modulationBlockSize);
          }
      
          template <int P> void setParameter(double v)
          {
              if constexpr (P == 0)
              {
                  SimpleReadWriteLock::ScopedWriteLock sl(topologyLock);
                  const auto nextShape = (int)std::round(v);
      
                  applyToVoices(
                      [nextShape](VoiceState& filter)
                      {
                          filter.setType(nextShape);
                      });
      
                  updatePlotFromFirstVoice(
                      [this, nextShape]
                      {
                          plotModel.setType(nextShape);
                      });
              }
              else if constexpr (P == 1)
              {
                  SimpleReadWriteLock::ScopedWriteLock sl(topologyLock);
                  const auto nextSlope = (int)std::round(v);
      
                  applyToVoices(
                      [nextSlope](VoiceState& filter)
                      {
                          filter.setSlopeMode(nextSlope);
                      });
      
                  updatePlotFromFirstVoice(
                      [this, nextSlope]
                      {
                          plotModel.setSlopeMode(nextSlope);
                      });
              }
              else if constexpr (P == 2)
              {
                  const auto value = (float)v;
                  applyToVoices(
                      [value](VoiceState& filter)
                      {
                          filter.setFrequencyHz(value);
                      });
      
                  updatePlotFromFirstVoice(
                      [this, value]
                      {
                          plotModel.setFrequencyHz(value);
                      });
              }
              else if constexpr (P == 3)
              {
                  const auto value = (float)v;
                  applyToVoices(
                      [value](VoiceState& filter)
                      {
                          filter.setQ(value);
                      });
      
                  updatePlotFromFirstVoice(
                      [this, value]
                      {
                          plotModel.setQ(value);
                      });
              }
              else if constexpr (P == 4)
              {
                  const auto value = (float)v;
                  applyToVoices(
                      [value](VoiceState& filter)
                      {
                          filter.setGainDb(value);
                      });
      
                  updatePlotFromFirstVoice(
                      [this, value]
                      {
                          plotModel.setGainDb(value);
                      });
              }
          }
      
          void createParameters(ParameterDataList& data)
          {
              {
                  parameter::data p("Shape", { 0.0, 6.0, 1.0 });
                  StringArray names;
                  addTypeLabels(names);
                  p.setParameterValueNames(names);
                  registerCallback<0>(p);
                  p.setDefaultValue((double)Defaults::type);
                  data.add(std::move(p));
              }
              {
                  parameter::data p("Slope", { 0.0, 3.0, 1.0 });
                  StringArray names;
                  addSlopeLabels(names);
                  p.setParameterValueNames(names);
                  registerCallback<1>(p);
                  p.setDefaultValue((double)Defaults::slopeMode);
                  data.add(std::move(p));
              }
              {
                  parameter::data p("Frequency", { 20.0, 20000.0, 0.01 });
                  p.setSkewForCentre(1000.0);
                  p.info.textConverter = parameter::pod::TextValueConverters::Frequency;
                  registerCallback<2>(p);
                  p.setDefaultValue(Defaults::frequencyHz);
                  data.add(std::move(p));
              }
              {
                  parameter::data p("Q", { 0.025, 25.0, 0.001 });
                  p.setSkewForCentre(0.70710678);
                  registerCallback<3>(p);
                  p.setDefaultValue(Defaults::q);
                  data.add(std::move(p));
              }
              {
                  parameter::data p("Gain", { -30.0, 30.0, 0.01 });
                  p.info.textConverter = parameter::pod::TextValueConverters::Decibel;
                  registerCallback<4>(p);
                  p.setDefaultValue(Defaults::gainDb);
                  data.add(std::move(p));
              }
          }
      
      private:
          static constexpr int getNumTypes() noexcept
          {
              return griffin::modules::essentials::eq_filter::EQFilterDesign::getNumTypes();
          }
      
          template <typename Function> void applyToVoices(Function&& function)
          {
              // PolyData iteration follows HISE's poly callback.
              // Each active voice receives the parameter update in its own DSP state.
              for (auto& voice : voices)
                  function(voice);
          }
      
          template <typename Function> void updatePlotFromFirstVoice(Function&& function)
          {
              // The plot is shared UI state. The first HISE voice owns graph updates
              // so voices do not compete for the displayed response.
              if (! voices.isVoiceRenderingActive() || voices.isFirst())
              {
                  function();
                  sendCoefficientUpdateMessage();
              }
          }
      
          static void addTypeLabels(StringArray& names)
          {
              names.add("Lowpass");
              names.add("Highpass");
              names.add("Bandpass");
              names.add("Notch");
              names.add("Bell");
              names.add("Low Shelf");
              names.add("High Shelf");
          }
      
          static void addSlopeLabels(StringArray& names)
          {
              names.add("12 dB/oct");
              names.add("24 dB/oct");
              names.add("48 dB/oct");
              names.add("96 dB/oct");
          }
      
          envelope::silent_killer<NV> voiceManager;
      };
      } // namespace project
      
      
      
      posted in C++ Development
      griffinboyG
      griffinboy
    • RE: Matrix modulation connection is broken in exported plugin

      @ustk Thanks for your hard work!

      posted in Bug Reports
      griffinboyG
      griffinboy
    • RE: Matrix modulation connection is broken in exported plugin

      @ustk
      I've had issues with the matrix modulator, and exporting and also some issues that appear in Hise, but I've not finished compiling my report yet. I believe there are a few issues, that I intended to make example projects for and send to Christoph. I know he's busy so I wanted to make it a useful report before I posted anything, but yeah I do know of some issues. I'm not sure if there ones you're experiencing are related to mine, but they might be

      posted in Bug Reports
      griffinboyG
      griffinboy
    • RE: I made a really good sounding JUNO-6 emulation for free, shared it in the KVR forum - this is what happened

      @Morphoice

      Sorry to hear that!
      Yeah I understand that experience. Audio plugins come under horrible fire no matter what they are. I'm not sure of the full reasons myself. I sometimes get into debates to counteract this kind of "all negatives no positives feedback" whenever I see it online.

      It's possible not many of these negative messages were aggressive, It might be the case that it was intended to be genuine feedback. But I've not seen the messages so I can't really speak to that.

      You probably don't need to be told, but that download count is great, keep it up.

      posted in General Questions
      griffinboyG
      griffinboy
    • RE: I am making an open source audiovisual modular with in->out->in routing

      @elanhickler Interesting work, thank you

      posted in C++ Development
      griffinboyG
      griffinboy
    • RE: Verb Factory

      @Orvillain Nice work

      posted in C++ Development
      griffinboyG
      griffinboy