I’m getting started with audio plugin development and looking to create a basic guitar amp plugin. Right now I’m trying to work out how to construct the tone stack portion. This would have to involve several filters working in parallel and then being summed together for the resultant output from the tone stack. But I’m not sure how this would be accomplished code-wise. My best guess is parallel ProcessorChains, but what’s the best way to do this? What kind of syntax is required to add together different parallel processes? I’m pretty new to C++ in general so apologies for the n00b question but, alas, we’ve all gotta start somewhere.
And I’ve worked through some of the JUCE tutorials and have gotten a good sense of how the basic mechanics work, but their tutorials really don’t go far enough for what I’m looking to do.
For a beginner, I would not recommend starting with ProcessorChains, as that adds another level of abstraction to what you’re trying to do. Instead, you can just have your processor own the 3 filter objects, and then step through your
processBlock buffer one sample at a time, calculating the output of
IIRFilter::processSingleSampleRaw for each filter object. Then the answer to:
…is just a simple
+, to sum the 3 filter outputs into a single output value, which you write back to the
processBlock buffer at the same sample position that you read the input sample from.
A couple caveats:
If you’re running 3 filters in parallel, you might want to divide that sum by 3, to scale your output signal back down.
IIRFilterclass I mentioned above is just one of many filter classes you could use here. If you’re doing a guitar tone stack, those are often simpler 6dB/oct (aka one pole) filters, rather than the steeper 12dB/oct (two pole) that an IIRFilter would give you. But I’d recommend just getting it up and running with
IIRFilter, since that’s a bit simpler than using one of the JUCE DSP classes (e.g. in this case, probably
dsp::FirstOrderTPTFilterwould be the best match). It will be straightforward enough to swap that out later once you get the basic setup working.
If you don’t already have a good C++ starting guide, you could do worse than Modern C++ for Absolute Beginners
Thanks for the advice. What’s the advantage to processing single samples rather than the entire block?
Good question. For efficiency of processing, there is indeed a potential advantage of processing an entire block at once.
However, the advantage here of processing one sample at a time is that it protects you from making the beginner’s mistake of allocating
AudioBuffer objects in a realtime method (the
processBlock method). Because, if you were going to process entire blocks at once, you would need places to store that new data. There are a few different ways that could be approached in code, but one way or another, somewhere you’d need an “extra” buffer (or three). It is totally possible to create those buffers and process a block at a time, you just don’t want to do that inside
processBlock (why? see cardinal rule #3 here), so processing sample-at-a-time keeps you from having to worry about that at this point.
In contrast to creating a new
AudioBuffer in a realtime method, it is not problem to create new
floats to hold single samples of filter output, or to hold the sum of the filters.
Don’t try to process a signal sample at a time. Just make a loop over each sample, and that’s enough. You don’t need temporary space for anything more than the internal state of your filter.
For a tone stack, it’s usually not independent filters in parallel, but one filter that processes everything at the same time. Each “leg” changes the impedance on a follower opamp on its different branches, but you cannot process them independently as much as you’d like. So just write the equations down, start with a simple filter, as writing the equations for such a big filter is… tedious: AudioTK/ToneStackFilter.hxx at main · AudioTK/AudioTK · GitHub
And that one is an IIR, which is not great for varying parameters!
So start with being accustomed with IIR TDF2, then move to state variable filters, and then, only then, tackle the different tone stacks (they are all variations around the same model).
I’m not sure what you’re trying to say there - seems contradictory.
And I had the impression that the OP was a newbie to DSP as well as CPP, so starting by writing their own DSP equations was not what I think they wanted…
Well, yeah, but there are lots of people that do one sample at a time, like calling a virtual function for each sample when it’s definitely not what you want to do.
But if OP cannot write equations, then there is no way he will write a tone stack filter. Of course you should have good level in DSP and also good coding skills.
depends on the equations lol. but i’m not really looking to emulate any particular tone stack, just to make a custom one that i can tweak to taste
Still need to know more than basic DSP for that.
If you are just looking to mess around with an EQ section, and see what happens, then there’s no problem with running the IIR filters as you first said.
@matthieu-brucher is making the point that to write a bona-fide emulation of a passive tone stack circuit, it will take some more complex DSP work than just running 3 filters independently. Maybe that’s important to you, maybe not…