Browse Source

Refactored the IIRFilter class, breaking out the coefficients into an IIRCoefficients class. Also important to note: there was an error in the gain level used previously in the makeHighShelf, low-shelf, and bandpass methods. This is now corrected, so you may need to check that your old code didn't compensate for the error.

tags/2021-05-28
jules 12 years ago
parent
commit
f5ede30892
4 changed files with 214 additions and 191 deletions
  1. +125
    -119
      modules/juce_audio_basics/effects/juce_IIRFilter.cpp
  2. +86
    -69
      modules/juce_audio_basics/effects/juce_IIRFilter.h
  3. +2
    -2
      modules/juce_audio_basics/sources/juce_IIRFilterAudioSource.cpp
  4. +1
    -1
      modules/juce_audio_basics/sources/juce_IIRFilterAudioSource.h

+ 125
- 119
modules/juce_audio_basics/effects/juce_IIRFilter.cpp View File

@@ -29,74 +29,38 @@
#endif
//==============================================================================
IIRFilter::IIRFilter()
: active (false), v1 (0), v2 (0)
{
zeromem (coefficients, sizeof (coefficients));
}
IIRFilter::IIRFilter (const IIRFilter& other)
: active (other.active), v1 (0), v2 (0)
IIRCoefficients::IIRCoefficients() noexcept
{
const SpinLock::ScopedLockType sl (other.processLock);
memcpy (coefficients, other.coefficients, sizeof (coefficients));
zeromem (c, sizeof (c));
}
IIRFilter::~IIRFilter()
{
}
IIRCoefficients::~IIRCoefficients() noexcept {}
//==============================================================================
void IIRFilter::reset() noexcept
IIRCoefficients::IIRCoefficients (const IIRCoefficients& other) noexcept
{
const SpinLock::ScopedLockType sl (processLock);
v1 = v2 = 0;
memcpy (c, other.c, sizeof (c));
}
float IIRFilter::processSingleSampleRaw (const float in) noexcept
IIRCoefficients& IIRCoefficients::operator= (const IIRCoefficients& other) noexcept
{
float out = coefficients[0] * in + v1;
JUCE_SNAP_TO_ZERO (out);
v1 = coefficients[1] * in - coefficients[3] * out + v2;
v2 = coefficients[2] * in - coefficients[4] * out;
return out;
memcpy (c, other.c, sizeof (c));
return *this;
}
void IIRFilter::processSamples (float* const samples,
const int numSamples) noexcept
IIRCoefficients::IIRCoefficients (double c1, double c2, double c3,
double c4, double c5, double c6) noexcept
{
const SpinLock::ScopedLockType sl (processLock);
if (active)
{
const float c0 = coefficients[0];
const float c1 = coefficients[1];
const float c2 = coefficients[2];
const float c3 = coefficients[3];
const float c4 = coefficients[4];
float lv1 = v1, lv2 = v2;
for (int i = 0; i < numSamples; ++i)
{
const float in = samples[i];
const float out = c0 * in + lv1;
samples[i] = out;
lv1 = c1 * in - c3 * out + lv2;
lv2 = c2 * in - c4 * out;
}
const double a = 1.0 / c4;
JUCE_SNAP_TO_ZERO (lv1); v1 = lv1;
JUCE_SNAP_TO_ZERO (lv2); v2 = lv2;
}
c[0] = (float) (c1 * a);
c[1] = (float) (c2 * a);
c[2] = (float) (c3 * a);
c[3] = (float) (c5 * a);
c[4] = (float) (c6 * a);
}
//==============================================================================
void IIRFilter::makeLowPass (const double sampleRate,
const double frequency) noexcept
IIRCoefficients IIRCoefficients::makeLowPass (const double sampleRate,
const double frequency) noexcept
{
jassert (sampleRate > 0);
@@ -104,38 +68,38 @@ void IIRFilter::makeLowPass (const double sampleRate,
const double nSquared = n * n;
const double c1 = 1.0 / (1.0 + std::sqrt (2.0) * n + nSquared);
setCoefficients (c1,
c1 * 2.0f,
c1,
1.0,
c1 * 2.0 * (1.0 - nSquared),
c1 * (1.0 - std::sqrt (2.0) * n + nSquared));
return IIRCoefficients (c1,
c1 * 2.0f,
c1,
1.0,
c1 * 2.0 * (1.0 - nSquared),
c1 * (1.0 - std::sqrt (2.0) * n + nSquared));
}
void IIRFilter::makeHighPass (const double sampleRate,
const double frequency) noexcept
IIRCoefficients IIRCoefficients::makeHighPass (const double sampleRate,
const double frequency) noexcept
{
const double n = tan (double_Pi * frequency / sampleRate);
const double nSquared = n * n;
const double c1 = 1.0 / (1.0 + std::sqrt (2.0) * n + nSquared);
setCoefficients (c1,
c1 * -2.0f,
c1,
1.0,
c1 * 2.0 * (nSquared - 1.0),
c1 * (1.0 - std::sqrt (2.0) * n + nSquared));
return IIRCoefficients (c1,
c1 * -2.0f,
c1,
1.0,
c1 * 2.0 * (nSquared - 1.0),
c1 * (1.0 - std::sqrt (2.0) * n + nSquared));
}
void IIRFilter::makeLowShelf (const double sampleRate,
const double cutOffFrequency,
const double Q,
const float gainFactor) noexcept
IIRCoefficients IIRCoefficients::makeLowShelf (const double sampleRate,
const double cutOffFrequency,
const double Q,
const float gainFactor) noexcept
{
jassert (sampleRate > 0);
jassert (Q > 0);
const double A = jmax (0.0f, gainFactor);
const double A = jmax (0.0f, std::sqrt (gainFactor));
const double aminus1 = A - 1.0;
const double aplus1 = A + 1.0;
const double omega = (double_Pi * 2.0 * jmax (cutOffFrequency, 2.0)) / sampleRate;
@@ -143,23 +107,23 @@ void IIRFilter::makeLowShelf (const double sampleRate,
const double beta = std::sin (omega) * std::sqrt (A) / Q;
const double aminus1TimesCoso = aminus1 * coso;
setCoefficients (A * (aplus1 - aminus1TimesCoso + beta),
A * 2.0 * (aminus1 - aplus1 * coso),
A * (aplus1 - aminus1TimesCoso - beta),
aplus1 + aminus1TimesCoso + beta,
-2.0 * (aminus1 + aplus1 * coso),
aplus1 + aminus1TimesCoso - beta);
return IIRCoefficients (A * (aplus1 - aminus1TimesCoso + beta),
A * 2.0 * (aminus1 - aplus1 * coso),
A * (aplus1 - aminus1TimesCoso - beta),
aplus1 + aminus1TimesCoso + beta,
-2.0 * (aminus1 + aplus1 * coso),
aplus1 + aminus1TimesCoso - beta);
}
void IIRFilter::makeHighShelf (const double sampleRate,
const double cutOffFrequency,
const double Q,
const float gainFactor) noexcept
IIRCoefficients IIRCoefficients::makeHighShelf (const double sampleRate,
const double cutOffFrequency,
const double Q,
const float gainFactor) noexcept
{
jassert (sampleRate > 0);
jassert (Q > 0);
const double A = jmax (0.0f, gainFactor);
const double A = jmax (0.0f, std::sqrt (gainFactor));
const double aminus1 = A - 1.0;
const double aplus1 = A + 1.0;
const double omega = (double_Pi * 2.0 * jmax (cutOffFrequency, 2.0)) / sampleRate;
@@ -167,73 +131,115 @@ void IIRFilter::makeHighShelf (const double sampleRate,
const double beta = std::sin (omega) * std::sqrt (A) / Q;
const double aminus1TimesCoso = aminus1 * coso;
setCoefficients (A * (aplus1 + aminus1TimesCoso + beta),
A * -2.0 * (aminus1 + aplus1 * coso),
A * (aplus1 + aminus1TimesCoso - beta),
aplus1 - aminus1TimesCoso + beta,
2.0 * (aminus1 - aplus1 * coso),
aplus1 - aminus1TimesCoso - beta);
return IIRCoefficients (A * (aplus1 + aminus1TimesCoso + beta),
A * -2.0 * (aminus1 + aplus1 * coso),
A * (aplus1 + aminus1TimesCoso - beta),
aplus1 - aminus1TimesCoso + beta,
2.0 * (aminus1 - aplus1 * coso),
aplus1 - aminus1TimesCoso - beta);
}
void IIRFilter::makeBandPass (const double sampleRate,
const double centreFrequency,
const double Q,
const float gainFactor) noexcept
IIRCoefficients IIRCoefficients::makePeakFilter (const double sampleRate,
const double centreFrequency,
const double Q,
const float gainFactor) noexcept
{
jassert (sampleRate > 0);
jassert (Q > 0);
const double A = jmax (0.0f, gainFactor);
const double A = jmax (0.0f, std::sqrt (gainFactor));
const double omega = (double_Pi * 2.0 * jmax (centreFrequency, 2.0)) / sampleRate;
const double alpha = 0.5 * std::sin (omega) / Q;
const double c2 = -2.0 * std::cos (omega);
const double alphaTimesA = alpha * A;
const double alphaOverA = alpha / A;
setCoefficients (1.0 + alphaTimesA,
c2,
1.0 - alphaTimesA,
1.0 + alphaOverA,
c2,
1.0 - alphaOverA);
return IIRCoefficients (1.0 + alphaTimesA,
c2,
1.0 - alphaTimesA,
1.0 + alphaOverA,
c2,
1.0 - alphaOverA);
}
//==============================================================================
IIRFilter::IIRFilter()
: active (false), v1 (0), v2 (0)
{
}
IIRFilter::IIRFilter (const IIRFilter& other)
: active (other.active), v1 (0), v2 (0)
{
const SpinLock::ScopedLockType sl (other.processLock);
coefficients = other.coefficients;
}
IIRFilter::~IIRFilter()
{
}
//==============================================================================
void IIRFilter::makeInactive() noexcept
{
const SpinLock::ScopedLockType sl (processLock);
active = false;
}
//==============================================================================
void IIRFilter::copyCoefficientsFrom (const IIRFilter& other) noexcept
void IIRFilter::setCoefficients (const IIRCoefficients& newCoefficients) noexcept
{
const SpinLock::ScopedLockType sl (processLock);
memcpy (coefficients, other.coefficients, sizeof (coefficients));
active = other.active;
coefficients = newCoefficients;
active = true;
}
//==============================================================================
void IIRFilter::setCoefficients (double c1, double c2, double c3,
double c4, double c5, double c6) noexcept
void IIRFilter::reset() noexcept
{
const double a = 1.0 / c4;
const SpinLock::ScopedLockType sl (processLock);
v1 = v2 = 0;
}
float IIRFilter::processSingleSampleRaw (const float in) noexcept
{
float out = coefficients.c[0] * in + v1;
JUCE_SNAP_TO_ZERO (out);
c1 *= a;
c2 *= a;
c3 *= a;
c5 *= a;
c6 *= a;
v1 = coefficients.c[1] * in - coefficients.c[3] * out + v2;
v2 = coefficients.c[2] * in - coefficients.c[4] * out;
return out;
}
void IIRFilter::processSamples (float* const samples,
const int numSamples) noexcept
{
const SpinLock::ScopedLockType sl (processLock);
coefficients[0] = (float) c1;
coefficients[1] = (float) c2;
coefficients[2] = (float) c3;
coefficients[3] = (float) c5;
coefficients[4] = (float) c6;
if (active)
{
const float c0 = coefficients.c[0];
const float c1 = coefficients.c[1];
const float c2 = coefficients.c[2];
const float c3 = coefficients.c[3];
const float c4 = coefficients.c[4];
float lv1 = v1, lv2 = v2;
active = true;
for (int i = 0; i < numSamples; ++i)
{
const float in = samples[i];
const float out = c0 * in + lv1;
samples[i] = out;
lv1 = c1 * in - c3 * out + lv2;
lv2 = c2 * in - c4 * out;
}
JUCE_SNAP_TO_ZERO (lv1); v1 = lv1;
JUCE_SNAP_TO_ZERO (lv2); v2 = lv2;
}
}
#undef JUCE_SNAP_TO_ZERO

+ 86
- 69
modules/juce_audio_basics/effects/juce_IIRFilter.h View File

@@ -25,13 +25,83 @@
#ifndef __JUCE_IIRFILTER_JUCEHEADER__
#define __JUCE_IIRFILTER_JUCEHEADER__
class IIRFilter;
//==============================================================================
/**
A set of coefficients for use in an IIRFilter object.
@see IIRFilter
*/
class JUCE_API IIRCoefficients
{
public:
//==============================================================================
/** Creates a null set of coefficients (which will produce silence). */
IIRCoefficients() noexcept;
/** Creates a copy of another filter. */
IIRCoefficients (const IIRCoefficients&) noexcept;
/** Creates a copy of another filter. */
IIRCoefficients& operator= (const IIRCoefficients&) noexcept;
/** Destructor. */
~IIRCoefficients() noexcept;
/** Returns the coefficients for a low-pass filter. */
static IIRCoefficients makeLowPass (double sampleRate,
double frequency) noexcept;
/** Returns the coefficients for a high-pass filter. */
static IIRCoefficients makeHighPass (double sampleRate,
double frequency) noexcept;
//==============================================================================
/** Returns the coefficients for a low-pass shelf filter with variable Q and gain.
The gain is a scale factor that the low frequencies are multiplied by, so values
greater than 1.0 will boost the low frequencies, values less than 1.0 will
attenuate them.
*/
static IIRCoefficients makeLowShelf (double sampleRate,
double cutOffFrequency,
double Q,
float gainFactor) noexcept;
/** Returns the coefficients for a high-pass shelf filter with variable Q and gain.
The gain is a scale factor that the high frequencies are multiplied by, so values
greater than 1.0 will boost the high frequencies, values less than 1.0 will
attenuate them.
*/
static IIRCoefficients makeHighShelf (double sampleRate,
double cutOffFrequency,
double Q,
float gainFactor) noexcept;
/** Returns the coefficients for a peak filter centred around a
given frequency, with a variable Q and gain.
The gain is a scale factor that the centre frequencies are multiplied by, so
values greater than 1.0 will boost the centre frequencies, values less than
1.0 will attenuate them.
*/
static IIRCoefficients makePeakFilter (double sampleRate,
double centreFrequency,
double Q,
float gainFactor) noexcept;
private:
friend class IIRFilter;
IIRCoefficients (double, double, double, double, double, double) noexcept;
float c[5];
};
//==============================================================================
/**
An IIR filter that can perform low, high, or band-pass filtering on an
audio signal.
@see IIRFilterAudioSource
@see IIRCoefficient, IIRFilterAudioSource
*/
class JUCE_API IIRFilter
{
@@ -40,8 +110,8 @@ public:
/** Creates a filter.
Initially the filter is inactive, so will have no effect on samples that
you process with it. Use the appropriate method to turn it into the type
of filter needed.
you process with it. Use the setCoefficients() method to turn it into the
type of filter needed.
*/
IIRFilter();
@@ -51,6 +121,16 @@ public:
/** Destructor. */
~IIRFilter();
//==============================================================================
/** Clears the filter so that any incoming data passes through unchanged. */
void makeInactive() noexcept;
/** Applies a set of coefficients to this filter. */
void setCoefficients (const IIRCoefficients& newCoefficients) noexcept;
/** Returns the coefficients that this filter is using. */
IIRCoefficients getCoefficients() const { return coefficients; }
//==============================================================================
/** Resets the filter's processing pipeline, ready to start a new stream of data.
@@ -60,10 +140,8 @@ public:
*/
void reset() noexcept;
/** Performs the filter operation on the given set of samples.
*/
void processSamples (float* samples,
int numSamples) noexcept;
/** Performs the filter operation on the given set of samples. */
void processSamples (float* samples, int numSamples) noexcept;
/** Processes a single sample, without any locking or checking.
@@ -72,74 +150,13 @@ public:
*/
float processSingleSampleRaw (float sample) noexcept;
//==============================================================================
/** Sets the filter up to act as a low-pass filter.
*/
void makeLowPass (double sampleRate,
double frequency) noexcept;
/** Sets the filter up to act as a high-pass filter.
*/
void makeHighPass (double sampleRate,
double frequency) noexcept;
//==============================================================================
/** Sets the filter up to act as a low-pass shelf filter with variable Q and gain.
The gain is a scale factor that the low frequencies are multiplied by, so values
greater than 1.0 will boost the low frequencies, values less than 1.0 will
attenuate them.
*/
void makeLowShelf (double sampleRate,
double cutOffFrequency,
double Q,
float gainFactor) noexcept;
/** Sets the filter up to act as a high-pass shelf filter with variable Q and gain.
The gain is a scale factor that the high frequencies are multiplied by, so values
greater than 1.0 will boost the high frequencies, values less than 1.0 will
attenuate them.
*/
void makeHighShelf (double sampleRate,
double cutOffFrequency,
double Q,
float gainFactor) noexcept;
/** Sets the filter up to act as a band pass filter centred around a
frequency, with a variable Q and gain.
The gain is a scale factor that the centre frequencies are multiplied by, so
values greater than 1.0 will boost the centre frequencies, values less than
1.0 will attenuate them.
*/
void makeBandPass (double sampleRate,
double centreFrequency,
double Q,
float gainFactor) noexcept;
/** Clears the filter's coefficients so that it becomes inactive.
*/
void makeInactive() noexcept;
//==============================================================================
/** Makes this filter duplicate the set-up of another one.
*/
void copyCoefficientsFrom (const IIRFilter& other) noexcept;
protected:
//==============================================================================
SpinLock processLock;
void setCoefficients (double c1, double c2, double c3,
double c4, double c5, double c6) noexcept;
bool active;
float coefficients[5];
IIRCoefficients coefficients;
float v1, v2;
// (use the copyCoefficientsFrom() method instead of this operator)
IIRFilter& operator= (const IIRFilter&);
JUCE_LEAK_DETECTOR (IIRFilter)
};


+ 2
- 2
modules/juce_audio_basics/sources/juce_IIRFilterAudioSource.cpp View File

@@ -35,10 +35,10 @@ IIRFilterAudioSource::IIRFilterAudioSource (AudioSource* const inputSource,
IIRFilterAudioSource::~IIRFilterAudioSource() {}
//==============================================================================
void IIRFilterAudioSource::setFilterParameters (const IIRFilter& newSettings)
void IIRFilterAudioSource::setCoefficients (const IIRCoefficients& newCoefficients)
{
for (int i = iirFilters.size(); --i >= 0;)
iirFilters.getUnchecked(i)->copyCoefficientsFrom (newSettings);
iirFilters.getUnchecked(i)->setCoefficients (newCoefficients);
}
//==============================================================================


+ 1
- 1
modules/juce_audio_basics/sources/juce_IIRFilterAudioSource.h View File

@@ -51,7 +51,7 @@ public:
//==============================================================================
/** Changes the filter to use the same parameters as the one being passed in. */
void setFilterParameters (const IIRFilter& newSettings);
void setCoefficients (const IIRCoefficients& newCoefficients);
//==============================================================================
void prepareToPlay (int samplesPerBlockExpected, double sampleRate);


Loading…
Cancel
Save