diff --git a/modules/juce_audio_basics/effects/juce_IIRFilter.cpp b/modules/juce_audio_basics/effects/juce_IIRFilter.cpp index 7bf4d762e8..e749e4598c 100644 --- a/modules/juce_audio_basics/effects/juce_IIRFilter.cpp +++ b/modules/juce_audio_basics/effects/juce_IIRFilter.cpp @@ -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 diff --git a/modules/juce_audio_basics/effects/juce_IIRFilter.h b/modules/juce_audio_basics/effects/juce_IIRFilter.h index 88700d1bdf..c85b90f82f 100644 --- a/modules/juce_audio_basics/effects/juce_IIRFilter.h +++ b/modules/juce_audio_basics/effects/juce_IIRFilter.h @@ -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) }; diff --git a/modules/juce_audio_basics/sources/juce_IIRFilterAudioSource.cpp b/modules/juce_audio_basics/sources/juce_IIRFilterAudioSource.cpp index 1eabb09005..4ee66f572c 100644 --- a/modules/juce_audio_basics/sources/juce_IIRFilterAudioSource.cpp +++ b/modules/juce_audio_basics/sources/juce_IIRFilterAudioSource.cpp @@ -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); } //============================================================================== diff --git a/modules/juce_audio_basics/sources/juce_IIRFilterAudioSource.h b/modules/juce_audio_basics/sources/juce_IIRFilterAudioSource.h index 7d940b0fdd..90796f395d 100644 --- a/modules/juce_audio_basics/sources/juce_IIRFilterAudioSource.h +++ b/modules/juce_audio_basics/sources/juce_IIRFilterAudioSource.h @@ -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);