Hi! In the JUCE IIRFilterDemo example (In the Example/DSP/ folder), when I change the parameters by moving the sliders, I can hear clicking noise. Is there a way to avoid that? With gain parameter for example, I can just smooth the value change with LinearSmoothedValue. But with IIRFilter, whenever there is a parameter change, I need to regenerate the coefficients. What would be the right way to update the coefficients without introducing clicking noise?
I also notice that the example code is updating the coefficient by calling *ProcessorDuplicator.state = coefficients. Will this have a thread safety issue?
void updateParameters()
{
if (sampleRate != 0.0)
{
auto cutoff = static_cast<float> (cutoffParam.getCurrentValue());
auto qVal = static_cast<float> (qParam.getCurrentValue());
switch (typeParam.getCurrentSelectedID())
{
case 1: *iir.state = *IIR::Coefficients<float>::makeLowPass (sampleRate, cutoff, qVal); break;
case 2: *iir.state = *IIR::Coefficients<float>::makeHighPass (sampleRate, cutoff, qVal); break;
case 3: *iir.state = *IIR::Coefficients<float>::makeBandPass (sampleRate, cutoff, qVal); break;
default: break;
}
}
}
Here is the source code from the JUCE IIRFilter example. Anyone knows how to smooth the cutoff parameter change? Currently when the slider value changes, the example simply calls:
*iir.state = *IIR::Coefficients<float>::makeLowPass (sampleRate, cutoff, qVal);
Which changes the coefficients value stored in the ProcessorDuplicator. I wonder what is the right way to smooth the coefficients value generation.
using namespace dsp;
//==============================================================================
struct IIRFilterDemoDSP
{
void prepare (const ProcessSpec& spec)
{
sampleRate = spec.sampleRate;
iir.state = IIR::Coefficients<float>::makeLowPass (sampleRate, 440.0);
iir.prepare (spec);
}
void process (const ProcessContextReplacing<float>& context)
{
iir.process (context);
}
void reset()
{
iir.reset();
}
void updateParameters()
{
if (sampleRate != 0.0)
{
auto cutoff = static_cast<float> (cutoffParam.getCurrentValue());
auto qVal = static_cast<float> (qParam.getCurrentValue());
switch (typeParam.getCurrentSelectedID())
{
case 1: *iir.state = *IIR::Coefficients<float>::makeLowPass (sampleRate, cutoff, qVal); break;
case 2: *iir.state = *IIR::Coefficients<float>::makeHighPass (sampleRate, cutoff, qVal); break;
case 3: *iir.state = *IIR::Coefficients<float>::makeBandPass (sampleRate, cutoff, qVal); break;
default: break;
}
}
}
//==============================================================================
ProcessorDuplicator<IIR::Filter<float>, IIR::Coefficients<float>> iir;
ChoiceParameter typeParam { { "Low-pass", "High-pass", "Band-pass" }, 1, "Type" };
SliderParameter cutoffParam { { 20.0, 20000.0 }, 0.5, 440.0f, "Cutoff", "Hz" };
SliderParameter qParam { { 0.3, 20.0 }, 0.5, 1.0 / std::sqrt(2.0), "Q" };
std::vector<DSPDemoParameterBase*> parameters { &typeParam, &cutoffParam, &qParam };
double sampleRate = 0.0;
};
struct IIRFilterDemo : public Component
{
IIRFilterDemo()
{
addAndMakeVisible (fileReaderComponent);
setSize (750, 500);
}
void resized() override
{
fileReaderComponent.setBounds (getLocalBounds());
}
AudioFileReaderComponent<IIRFilterDemoDSP> fileReaderComponent;
};