Three ways to make IIR-filter, which to choose?

So I would like to start using IIR-filters but I’m a bit confused by the multiple options for the predefined Juce-IIR filters.

Let’s say I want to make a highpass. I found at least three similar but not identical ways to call makeHighPass What are the differences? I’m not sure where to start here. I primarily intend to use them in plugins.

static std::array<NumericType, 6> dsp::IIR::ArrayCoefficients< NumericType >::makeHighPass (...
static Ptr dsp::IIR::Coefficients< NumericType >::makeHighPass (...
static IIRCoefficients IIRCoefficients::makeHighPass ( ...

Links:
https://docs.juce.com/master/structdsp_1_1IIR_1_1ArrayCoefficients.html#ae6d6fee439a02ae904917ab1e096528b
https://docs.juce.com/master/structdsp_1_1IIR_1_1Coefficients.html#a7d46d898caab5b76e169460e0a4651e6
https://docs.juce.com/master/classIIRCoefficients.html#a19027ed08d1483e83aafc7aa287cdcb0

In this thread, from 2017, the last method is used.

I found this thread which seems to answer a great deal. :sunglasses:

Regarding the ArrayCoefficients: These were added as a heap allocation free alternative to the original dsp::IIR::Coefficients class member functions in order to have a way to compute coefficients safely on the audio thread. I use them here together with a dsp::ProcessorDuplicator<juce::dsp::IIR::Filter<float>, juce::dsp::IIR::Coefficients<float>> if you want to have a starting point:

Thanks, that looks interesting although a little bit too complicated for me to unravel right now. Also Im not sure I understand the ramification of “heap allocation free” altough I have some understanding of heap vs. stack. But I’m not sure when I should use one or the other.

If you create the coefficients in the audio thread you should use the heap allocation free version.
Allocating memory from a realtime thread is dangerous, because the allocation can take an indeterministic amount of time. Heap allocations are synchronised with other threads which might not have the same high priority.

First of all, I just noticed that I shared a different source file than intended, as this actually does not re-compute coefficients during processing but switches between two precomputed coefficient sets by re-assigning the processor duplicatord state member. To break it down, the main point is

// Declare a multi channel capable filter itself like this
juce::dsp::ProcessorDuplicator<juce::dsp::IIR::Filter<float>, juce::dsp::IIR::Coefficients<float>> filter;

// Assign new coefficients like this
*filter.state = juce::dsp:ArrayCoefficients<float>::makeXY (foo, bar);

Then regarding stack vs heap, Daniel already mentioned the important difference. Tbh. I would always go for the ArrayCoefficients, there is no real downside using it as default.

Thanks for the explanations. Maybe it’s because I’ve only made some quite simple prototypes (mainly plugins) but I’ve never created any variables inside the actual process block. I’m not sure when I would do that.

Since I have the AudioProcessor encapsulated in it’s own class, I’ve usually created (private) variables for audio calculations (gain, frequency e.t.c.) on the class level. In that case I would guess their creation don’t affect the audio thread but maybe I miss something here?

So far I’ve tried to keep the process blocks as clean as possible. Only for pure audio calculations. I even found the idea of calling a wrapper (buffer to audioblock) a bit suspicious.

Where is the number of channels set in your example? I don’t see it.

It is when you prepare the filter using

juce::dsp::ProcessSpec spec;
spec.sampleRate = sampleRate;
spec.maximumBlockSize = estimatedBufferSize;
spec.numChannels = getMainBusNumInputChannels(); // or whatever in your case
filter.prepare (spec);

See: JUCE: dsp::ProcessSpec Struct Reference

The ProcessorDuplicator takes care to create a state for each channel.

OK, so the filter created in PluginPenguin’s example above is not “ready”. It gets its channel configuration etc. when the spec is given in prepare, and until then it can have any number of channels, sample rate e.t.c?

Yes, @PluginPenguin just posted a few relevant lines, thinking the prepare step would be already known.

Some Processors can work without the prepare, but you should call prepare before using it, since many require it.

Seems I don’t get this the right way altough it’s only two lines of code… :slightly_smiling_face:

So I try to use IIR as a Shelving, I belive it’s the only option for Shelving.

First line: You say I declare the filter (in the .h file for the processor?), so is this a pointer? Would I instanceiate it in the Processor constructor? I’ve tried to understand the documentation for ProcessDuplicator, but I’m not sure I get it.
Second line: You derecerence: *filter meaning filter is actually a pointer… Did I get that right?

Also, do I need to call the makeXYZFilter Methods every time a single parameter, like gain get’s updated? Seems I can’t acces single parameters using methods on IIR-filters the same way as with some other filters.

No, filter is actually a regular member. Thats why it says filter.state, not filter->state.

The keyword is operator precedence. The . is evaluated before the dereferencing. The * dereferences the StateType::Ptr. To make it explicit:

*(filter.state) = juce::dsp:ArrayCoefficients<float>::makeXY (foo, bar);

StateType you can find in your ProcessorDuplicator declaration, the second template argument:

juce::dsp::IIR::Coefficients<float>

Hope that helps

Yes, I think I begin to understand the terminology. The filter in PluginPenguin’s example is not the actual IIR-filter but an instance of ProcessDuplicator which in turn contains one or several (mono) dsp::IIR::filter-filters. These in turn are not the same as the class IIR-filter. What I don’t find is the superclass that dsp::IIR:Filter inherits from. For the other IIR-class it’s easier to see the chain.

How from documentation can I see that dsp::IIR::ArrayCoefficients< NumericType > is in fact compatible with the StateType::Ptr that the state member of dsp::ProcessorDuplicator requires according to the reference?
I don’t find anything when typing StateType in the Class Index.

That is because it doesn’t inherit anything. virtual calls would be too expensive in the audio code, that’s why it is using templates.
Instead of calling a function, the ProcessorDuplicator gets the Processor (or ProcessorChain) as template argument. The compiler can now create the whole pipeline at compile time and has a better chance to optimize, unroll loops etc.
Because the ProcessorDuplicator needs only one Processor, but a separate state for every instance, there is the second template argument, the StateType.

If you were to write your own Processor, it needs to implement the prepare, process and reset functions.

N.B. there is a base class it can inherit, which is ProcessorBase. But that is merely used to create a dynamic storage (buzz word type erasure). It is not mandatory for the ProcessorChain.

It is not compatible per se, you made it compatible by using it as template parameter in the ProcessorChain. Inside the code the dsp::IIR::Coefficients are now referred as StateType.
If there were a function missing you would know by a compiler error. But AFAIK it is just a storage, so it just needs an assignment operator.

Thanks for the explanation and patience. :grinning:

I still can’t find any description of StateType in the documentation. Not that it’s the most important aspect for me right now but this has happened a couple of times now that I don’t seem to find info on some types, classes or concepts and things in Juce. Is there somewhere else to look than in the Class Index?

No there isn’t. Think of StateType as a variable name which gets the value in the line you copied from PluginPenguin:

juce::dsp::ProcessorDuplicator<juce::dsp::IIR::Filter<float>, juce::dsp::IIR::Coefficients<float>> filter;

It says in the page you linked:

template<typename MonoProcessorType, typename StateType>
struct dsp::ProcessorDuplicator< MonoProcessorType, StateType >

The MonoProcessorType is now juce::dsp::IIR::Filter<float> and
the StateType is juce::dsp::IIR::Coefficients<float>.

The whole dsp module works by using classes/structs as template parameters.

I agree it is a bit difficult to wrap the head around those concepts at first. I had to read quite a few examples before I got it (if I got it at all, I am also still learning)

OK, I think I see what you mean. There are a lot of templates in Juce so it’s easy to get lost. Guess that’s the price for great flexibility; that it’s hard to follow the original intentions for the code structure.

The other things I didn’t find are probably template arguments (or whatever is the right word) too.

I realize I need to revisit this. To make sure I update parameters in a safe way. Hope someone can help me to confirm that I got this right…

I’ve implemented a IIR now in the fashion presented here and I’ve connected one parameter (gain) to the GUI via AudopProcessorValueTreeState as presented in tutorials. But since the parameter itself is of no use fore the processBlock I need to recalculate the IIR-coefficients whenever the parameter changes.

However I thought I was clever putting this calculation in a parameter callback using juce::AudioProcessorValueTreeState::Listener implementing it’s parameterChanged() and reassigning the filter’s (or rather process duplicator’s) state only when the GUI parameter changes.

*filter.state = juce::dsp:ArrayCoefficients<float>::makeHighShelf(....);

However reading this thread I realize this might be dangerous. So, as long as I don’t have too many parameters I guess instead I should call the makeHighShelf
in the audio thread for every processBlock regardless of wether any parameter has been updated or alternatively test for changes somehow there.

Am I on the right track here as long as I don’t use too many parameters? It seems a little bit heavy but maybe it’s ok? I find no other way without implementing rather complicated stuff.