StateVariableFilter - Fast Modulation

#1

Hey All,

I have a question on the SVF in JUCE. It says it should support fast modulation, however running the filter over a sine i’m getting lots of little pops & clicks. In the Ladder Filter I see it has the internal updateSmoothers(), but I don’t find this in the SVF. I tried to implement my own parameter smoothing, and switch to the single sample process callback, updating the cutoff before each call if it’s changed. Still it has the clicks : /

Has anyone else run into this issue? Is it something I’m doing incorrectly in the initialization, or is it too much to expect a totally transparent modulation over sine? The Ladder filter is effing amazing*! just curious if this has been on anyones radar?

Best,

J

0 Likes

#2

Could you share how you write your code ? You’re not supposed to have pops and clicks until something wrong is going on…

0 Likes

#3

Hey Ivan, I supposed that would be helpful! :stuck_out_tongue_closed_eyes:

So, in my .h file they’re declared as such:

float mPreviousLowpassCutoff;
float mPreviousHighpassCutoff;

std::unique_ptr<juce::dsp::StateVariableFilter::Filter<float>> mHighpassFilterL;
std::unique_ptr<juce::dsp::StateVariableFilter::Filter<float>> mHighpassFilterR;

std::unique_ptr<juce::dsp::StateVariableFilter::Filter<float>> mLowpassFilterL;
std::unique_ptr<juce::dsp::StateVariableFilter::Filter<float>> mLowpassFilterR;

I’m using one for each channel because I haven’t wrapped my head around the processorDuplicator quite yet.

Then in the prepareToPlay, they’re initialized as such:

dsp::ProcessSpec spec { sampleRate, (uint32)samplesPerBlock, 1 };

mHighpassFilterL.reset(new juce::dsp::StateVariableFilter::Filter<float>());
mHighpassFilterL->prepare(spec);
mHighpassFilterL->parameters->type = juce::dsp::StateVariableFilter::Parameters<float>::Type::highPass;

mHighpassFilterR.reset(new juce::dsp::StateVariableFilter::Filter<float>());
mHighpassFilterR->prepare(spec);
mHighpassFilterR->parameters->type = juce::dsp::StateVariableFilter::Parameters<float>::Type::highPass;

mLowpassFilterL.reset(new juce::dsp::StateVariableFilter::Filter<float>());
mLowpassFilterL->prepare(spec);
mLowpassFilterL->parameters->type = juce::dsp::StateVariableFilter::Parameters<float>::Type::lowPass;

mLowpassFilterR.reset(new juce::dsp::StateVariableFilter::Filter<float>());
mLowpassFilterR->prepare(spec);
mLowpassFilterR->parameters->type = juce::dsp::StateVariableFilter::Parameters<float>::Type::lowPass;

mPreviousLowpassCutoff = 0;
mPreviousHighpassCutoff = 0;

And finally in the process block they’re being used like so:

if (mapped_LowpassCutoff != mPreviousLowpassCutoff) {
    mLowpassFilterL->parameters->setCutOffFrequency(getSampleRate(), mapped_LowpassCutoff);
    mLowpassFilterR->parameters->setCutOffFrequency(getSampleRate(), mapped_LowpassCutoff);
    mPreviousLowpassCutoff = mapped_LowpassCutoff;
}

if (mapped_HighpassCutoff != mPreviousHighpassCutoff) {
    mHighpassFilterL->parameters->setCutOffFrequency(getSampleRate(), mapped_HighpassCutoff);
    mHighpassFilterR->parameters->setCutOffFrequency(getSampleRate(), mapped_HighpassCutoff);
    mPreviousHighpassCutoff = mapped_HighpassCutoff;
}

dsp::AudioBlock<float> leftBlock(inAudioBuffer);
leftBlock = leftBlock.getSingleChannelBlock(OutputBusBuses::WetAudioIn0);

dsp::AudioBlock<float> rightBlock(inAudioBuffer);
rightBlock = rightBlock.getSingleChannelBlock(OutputBusBuses::WetAudioIn1);

mHighpassFilterL->process(dsp::ProcessContextReplacing<float>(leftBlock));
mHighpassFilterR->process(dsp::ProcessContextReplacing<float>(rightBlock));

mLowpassFilterL->process(dsp::ProcessContextReplacing<float>(leftBlock));
mLowpassFilterR->process(dsp::ProcessContextReplacing<float>(rightBlock));

I’m a bit new to all of the new DSP classes so lmk if any of this is looking funny. If it’s not the repro for what i’m experiencing is just playing a sine wave and then turning the cutoff knob.

In the above code, there’s no parameter smoothing applied, however, I did have another version of this code using the single sample process function with sample rate smoothing and the issue seemed to persist.

Thanks Ivan!

0 Likes

#4

Hello ! I have a hard time finding what’s wrong here, the issue might be somewhere else…

I’ve just created a new plug-in project in JUCE, where I put your code, here it is:

TestProject.zip (5.5 KB)

Could you tell me if you experience clicks or artefacts with this ?

1 Like

#5

Hey Ivan,

Thanks for putting this together and helping check it out.

The only change I added to the project was adding an AU scheme as I don’t have the VST2 SDK handy here.

After loading the AU in ableton, it does have the same issue. Playing a sine wave through, and turning the knobs, I can hear crackles added to the signal.

As a control dropping auto filter, or the JUCE ladder filter doesn’t exhibit the same results.

I could perhaps be being extremely picky, I’m sure that this isn’t audible in signals with a lot of harmonic content. The filters are great and useable in that context, but wondering if there’s a way to make them :100: on low harmonic signals too?

0 Likes

#6

Could you send me your sine wave in an audio file ?

Other questions :

  • Do you have the same issue if you increase the latency in Ableton Live ?
  • Do you have the same issue in the DAW Reaper (you can load AUs there too) ?
  • Is your plug-in compiled in Release configuration ?
0 Likes

#7

Incidentally, I am having a similar clicks and pops issue. It’s most noticeable when changing the filter cut off from a GUI knob. I am reasonably sure this isn’t a thread safety issue because the filter settings are updated in the audio thread. When changing the cut off from my internal LFOs (also updated on the audio thread), the issue isn’t as bad. (The GUI knob probably causes larger cut off frequency changes than the LFOs because the cut off parameter has a skew applied to it. My LFOs don’t yet deal with the parameter skews. Maybe the StateVariableFilter doesn’t deal well with large and abrupt changes of the frequency…?)

Testing this currently on Windows, almost latest Visual Studio 2017, the issue is the same with debug and release builds. The cut off parameter is handled via AudioProcessorValueTreeState and Slider attachment.

0 Likes

#8

@Xenakios : do you have the same issue with my example project as well ?

By the way, the StateVariableFilter class is not supposed to remove all the noise incoming from cutoff frequency changes. It removes efficiently the kind of artefacts you would have with IIR::Filter with the output amplitude being superior than 0 dB during the change. I’ve made some demonstration of this in my ADC16 video about IIR filters and TPT structure.

If you have a fast LFO modulating the frequency with some high depth and say a square waveform, you will still hear some “plops”-like sounds, and you’ll probably need to smooth a little the LFO, but not of the artefact kind. If it’s something like a click, and if you can hear it by changing a little the frequency with a GUI knob, then something is wrong.

0 Likes

#9

Hmmmm, I see. Listening, I agree these could be called “plops” & not “pops & clicks” I think it’s working as intended in that case I just wanted make certain I was using them properly!

Apologies and thanks for the explanation Ivan.

0 Likes