1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-10 23:44:24 +00:00

IIRFilter: Fix potential divide by zero

This commit is contained in:
Anthony Nicholls 2023-08-18 15:40:42 +01:00
parent 2aff537ced
commit f2e03eade0
3 changed files with 101 additions and 83 deletions

View file

@ -60,6 +60,20 @@ public:
: minusInfinityDb; : minusInfinityDb;
} }
/** Restricts a gain value based on a lower bound specified in dBFS.
This is useful if you want to make sure a gain value never reaches zero.
*/
template <typename Type>
static Type gainWithLowerBound (Type gain, Type lowerBoundDb)
{
// You probably want to use a negative decibel value or the gain will
// be restricted to boosting only!
jassert (lowerBoundDb < (Type) 0.0);
return jmax ((Type) gain, Decibels::decibelsToGain (lowerBoundDb, lowerBoundDb - (Type) 1.0));
}
//============================================================================== //==============================================================================
/** Converts a decibel reading to a string. /** Converts a decibel reading to a string.

View file

@ -23,6 +23,8 @@
namespace juce namespace juce
{ {
constexpr auto minimumDecibels = -300.0f;
IIRCoefficients::IIRCoefficients() noexcept IIRCoefficients::IIRCoefficients() noexcept
{ {
zeromem (coefficients, sizeof (coefficients)); zeromem (coefficients, sizeof (coefficients));
@ -44,7 +46,7 @@ IIRCoefficients& IIRCoefficients::operator= (const IIRCoefficients& other) noexc
IIRCoefficients::IIRCoefficients (double c1, double c2, double c3, IIRCoefficients::IIRCoefficients (double c1, double c2, double c3,
double c4, double c5, double c6) noexcept double c4, double c5, double c6) noexcept
{ {
auto a = 1.0 / c4; const auto a = 1.0 / c4;
coefficients[0] = (float) (c1 * a); coefficients[0] = (float) (c1 * a);
coefficients[1] = (float) (c2 * a); coefficients[1] = (float) (c2 * a);
@ -67,9 +69,9 @@ IIRCoefficients IIRCoefficients::makeLowPass (double sampleRate,
jassert (frequency > 0.0 && frequency <= sampleRate * 0.5); jassert (frequency > 0.0 && frequency <= sampleRate * 0.5);
jassert (Q > 0.0); jassert (Q > 0.0);
auto n = 1.0 / std::tan (MathConstants<double>::pi * frequency / sampleRate); const auto n = 1.0 / std::tan (MathConstants<double>::pi * frequency / sampleRate);
auto nSquared = n * n; const auto nSquared = n * n;
auto c1 = 1.0 / (1.0 + 1.0 / Q * n + nSquared); const auto c1 = 1.0 / (1.0 + 1.0 / Q * n + nSquared);
return IIRCoefficients (c1, return IIRCoefficients (c1,
c1 * 2.0, c1 * 2.0,
@ -93,9 +95,9 @@ IIRCoefficients IIRCoefficients::makeHighPass (double sampleRate,
jassert (frequency > 0.0 && frequency <= sampleRate * 0.5); jassert (frequency > 0.0 && frequency <= sampleRate * 0.5);
jassert (Q > 0.0); jassert (Q > 0.0);
auto n = std::tan (MathConstants<double>::pi * frequency / sampleRate); const auto n = std::tan (MathConstants<double>::pi * frequency / sampleRate);
auto nSquared = n * n; const auto nSquared = n * n;
auto c1 = 1.0 / (1.0 + 1.0 / Q * n + nSquared); const auto c1 = 1.0 / (1.0 + 1.0 / Q * n + nSquared);
return IIRCoefficients (c1, return IIRCoefficients (c1,
c1 * -2.0, c1 * -2.0,
@ -119,9 +121,9 @@ IIRCoefficients IIRCoefficients::makeBandPass (double sampleRate,
jassert (frequency > 0.0 && frequency <= sampleRate * 0.5); jassert (frequency > 0.0 && frequency <= sampleRate * 0.5);
jassert (Q > 0.0); jassert (Q > 0.0);
auto n = 1.0 / std::tan (MathConstants<double>::pi * frequency / sampleRate); const auto n = 1.0 / std::tan (MathConstants<double>::pi * frequency / sampleRate);
auto nSquared = n * n; const auto nSquared = n * n;
auto c1 = 1.0 / (1.0 + 1.0 / Q * n + nSquared); const auto c1 = 1.0 / (1.0 + 1.0 / Q * n + nSquared);
return IIRCoefficients (c1 * n / Q, return IIRCoefficients (c1 * n / Q,
0.0, 0.0,
@ -145,9 +147,9 @@ IIRCoefficients IIRCoefficients::makeNotchFilter (double sampleRate,
jassert (frequency > 0.0 && frequency <= sampleRate * 0.5); jassert (frequency > 0.0 && frequency <= sampleRate * 0.5);
jassert (Q > 0.0); jassert (Q > 0.0);
auto n = 1.0 / std::tan (MathConstants<double>::pi * frequency / sampleRate); const auto n = 1.0 / std::tan (MathConstants<double>::pi * frequency / sampleRate);
auto nSquared = n * n; const auto nSquared = n * n;
auto c1 = 1.0 / (1.0 + n / Q + nSquared); const auto c1 = 1.0 / (1.0 + n / Q + nSquared);
return IIRCoefficients (c1 * (1.0 + nSquared), return IIRCoefficients (c1 * (1.0 + nSquared),
2.0 * c1 * (1.0 - nSquared), 2.0 * c1 * (1.0 - nSquared),
@ -171,9 +173,9 @@ IIRCoefficients IIRCoefficients::makeAllPass (double sampleRate,
jassert (frequency > 0.0 && frequency <= sampleRate * 0.5); jassert (frequency > 0.0 && frequency <= sampleRate * 0.5);
jassert (Q > 0.0); jassert (Q > 0.0);
auto n = 1.0 / std::tan (MathConstants<double>::pi * frequency / sampleRate); const auto n = 1.0 / std::tan (MathConstants<double>::pi * frequency / sampleRate);
auto nSquared = n * n; const auto nSquared = n * n;
auto c1 = 1.0 / (1.0 + 1.0 / Q * n + nSquared); const auto c1 = 1.0 / (1.0 + 1.0 / Q * n + nSquared);
return IIRCoefficients (c1 * (1.0 - n / Q + nSquared), return IIRCoefficients (c1 * (1.0 - n / Q + nSquared),
c1 * 2.0 * (1.0 - nSquared), c1 * 2.0 * (1.0 - nSquared),
@ -192,13 +194,13 @@ IIRCoefficients IIRCoefficients::makeLowShelf (double sampleRate,
jassert (cutOffFrequency > 0.0 && cutOffFrequency <= sampleRate * 0.5); jassert (cutOffFrequency > 0.0 && cutOffFrequency <= sampleRate * 0.5);
jassert (Q > 0.0); jassert (Q > 0.0);
auto A = jmax (0.0f, std::sqrt (gainFactor)); const auto A = std::sqrt (Decibels::gainWithLowerBound (gainFactor, minimumDecibels));
auto aminus1 = A - 1.0; const auto aminus1 = A - 1.0;
auto aplus1 = A + 1.0; const auto aplus1 = A + 1.0;
auto omega = (MathConstants<double>::twoPi * jmax (cutOffFrequency, 2.0)) / sampleRate; const auto omega = (MathConstants<double>::twoPi * jmax (cutOffFrequency, 2.0)) / sampleRate;
auto coso = std::cos (omega); const auto coso = std::cos (omega);
auto beta = std::sin (omega) * std::sqrt (A) / Q; const auto beta = std::sin (omega) * std::sqrt (A) / Q;
auto aminus1TimesCoso = aminus1 * coso; const auto aminus1TimesCoso = aminus1 * coso;
return IIRCoefficients (A * (aplus1 - aminus1TimesCoso + beta), return IIRCoefficients (A * (aplus1 - aminus1TimesCoso + beta),
A * 2.0 * (aminus1 - aplus1 * coso), A * 2.0 * (aminus1 - aplus1 * coso),
@ -217,13 +219,13 @@ IIRCoefficients IIRCoefficients::makeHighShelf (double sampleRate,
jassert (cutOffFrequency > 0.0 && cutOffFrequency <= sampleRate * 0.5); jassert (cutOffFrequency > 0.0 && cutOffFrequency <= sampleRate * 0.5);
jassert (Q > 0.0); jassert (Q > 0.0);
auto A = jmax (0.0f, std::sqrt (gainFactor)); const auto A = std::sqrt (Decibels::gainWithLowerBound (gainFactor, minimumDecibels));
auto aminus1 = A - 1.0; const auto aminus1 = A - 1.0;
auto aplus1 = A + 1.0; const auto aplus1 = A + 1.0;
auto omega = (MathConstants<double>::twoPi * jmax (cutOffFrequency, 2.0)) / sampleRate; const auto omega = (MathConstants<double>::twoPi * jmax (cutOffFrequency, 2.0)) / sampleRate;
auto coso = std::cos (omega); const auto coso = std::cos (omega);
auto beta = std::sin (omega) * std::sqrt (A) / Q; const auto beta = std::sin (omega) * std::sqrt (A) / Q;
auto aminus1TimesCoso = aminus1 * coso; const auto aminus1TimesCoso = aminus1 * coso;
return IIRCoefficients (A * (aplus1 + aminus1TimesCoso + beta), return IIRCoefficients (A * (aplus1 + aminus1TimesCoso + beta),
A * -2.0 * (aminus1 + aplus1 * coso), A * -2.0 * (aminus1 + aplus1 * coso),
@ -242,12 +244,12 @@ IIRCoefficients IIRCoefficients::makePeakFilter (double sampleRate,
jassert (frequency > 0.0 && frequency <= sampleRate * 0.5); jassert (frequency > 0.0 && frequency <= sampleRate * 0.5);
jassert (Q > 0.0); jassert (Q > 0.0);
auto A = jmax (0.0f, std::sqrt (gainFactor)); const auto A = std::sqrt (Decibels::gainWithLowerBound (gainFactor, minimumDecibels));
auto omega = (MathConstants<double>::twoPi * jmax (frequency, 2.0)) / sampleRate; const auto omega = (MathConstants<double>::twoPi * jmax (frequency, 2.0)) / sampleRate;
auto alpha = 0.5 * std::sin (omega) / Q; const auto alpha = 0.5 * std::sin (omega) / Q;
auto c2 = -2.0 * std::cos (omega); const auto c2 = -2.0 * std::cos (omega);
auto alphaTimesA = alpha * A; const auto alphaTimesA = alpha * A;
auto alphaOverA = alpha / A; const auto alphaOverA = alpha / A;
return IIRCoefficients (1.0 + alphaTimesA, return IIRCoefficients (1.0 + alphaTimesA,
c2, c2,

View file

@ -30,6 +30,8 @@ namespace dsp
namespace IIR namespace IIR
{ {
constexpr auto minimumDecibels = -300.0;
template <typename NumericType> template <typename NumericType>
std::array<NumericType, 4> ArrayCoefficients<NumericType>::makeFirstOrderLowPass (double sampleRate, std::array<NumericType, 4> ArrayCoefficients<NumericType>::makeFirstOrderLowPass (double sampleRate,
NumericType frequency) NumericType frequency)
@ -37,7 +39,7 @@ std::array<NumericType, 4> ArrayCoefficients<NumericType>::makeFirstOrderLowPass
jassert (sampleRate > 0.0); jassert (sampleRate > 0.0);
jassert (frequency > 0 && frequency <= static_cast<float> (sampleRate * 0.5)); jassert (frequency > 0 && frequency <= static_cast<float> (sampleRate * 0.5));
auto n = std::tan (MathConstants<NumericType>::pi * frequency / static_cast<NumericType> (sampleRate)); const auto n = std::tan (MathConstants<NumericType>::pi * frequency / static_cast<NumericType> (sampleRate));
return { { n, n, n + 1, n - 1 } }; return { { n, n, n + 1, n - 1 } };
} }
@ -49,7 +51,7 @@ std::array<NumericType, 4> ArrayCoefficients<NumericType>::makeFirstOrderHighPas
jassert (sampleRate > 0.0); jassert (sampleRate > 0.0);
jassert (frequency > 0 && frequency <= static_cast<float> (sampleRate * 0.5)); jassert (frequency > 0 && frequency <= static_cast<float> (sampleRate * 0.5));
auto n = std::tan (MathConstants<NumericType>::pi * frequency / static_cast<NumericType> (sampleRate)); const auto n = std::tan (MathConstants<NumericType>::pi * frequency / static_cast<NumericType> (sampleRate));
return { { 1, -1, n + 1, n - 1 } }; return { { 1, -1, n + 1, n - 1 } };
} }
@ -61,7 +63,7 @@ std::array<NumericType, 4> ArrayCoefficients<NumericType>::makeFirstOrderAllPass
jassert (sampleRate > 0.0); jassert (sampleRate > 0.0);
jassert (frequency > 0 && frequency <= static_cast<float> (sampleRate * 0.5)); jassert (frequency > 0 && frequency <= static_cast<float> (sampleRate * 0.5));
auto n = std::tan (MathConstants<NumericType>::pi * frequency / static_cast<NumericType> (sampleRate)); const auto n = std::tan (MathConstants<NumericType>::pi * frequency / static_cast<NumericType> (sampleRate));
return { { n - 1, n + 1, n + 1, n - 1 } }; return { { n - 1, n + 1, n + 1, n - 1 } };
} }
@ -82,10 +84,10 @@ std::array<NumericType, 6> ArrayCoefficients<NumericType>::makeLowPass (double s
jassert (frequency > 0 && frequency <= static_cast<float> (sampleRate * 0.5)); jassert (frequency > 0 && frequency <= static_cast<float> (sampleRate * 0.5));
jassert (Q > 0.0); jassert (Q > 0.0);
auto n = 1 / std::tan (MathConstants<NumericType>::pi * frequency / static_cast<NumericType> (sampleRate)); const auto n = 1 / std::tan (MathConstants<NumericType>::pi * frequency / static_cast<NumericType> (sampleRate));
auto nSquared = n * n; const auto nSquared = n * n;
auto invQ = 1 / Q; const auto invQ = 1 / Q;
auto c1 = 1 / (1 + invQ * n + nSquared); const auto c1 = 1 / (1 + invQ * n + nSquared);
return { { c1, c1 * 2, c1, return { { c1, c1 * 2, c1,
1, c1 * 2 * (1 - nSquared), 1, c1 * 2 * (1 - nSquared),
@ -108,10 +110,10 @@ std::array<NumericType, 6> ArrayCoefficients<NumericType>::makeHighPass (double
jassert (frequency > 0 && frequency <= static_cast<float> (sampleRate * 0.5)); jassert (frequency > 0 && frequency <= static_cast<float> (sampleRate * 0.5));
jassert (Q > 0.0); jassert (Q > 0.0);
auto n = std::tan (MathConstants<NumericType>::pi * frequency / static_cast<NumericType> (sampleRate)); const auto n = std::tan (MathConstants<NumericType>::pi * frequency / static_cast<NumericType> (sampleRate));
auto nSquared = n * n; const auto nSquared = n * n;
auto invQ = 1 / Q; const auto invQ = 1 / Q;
auto c1 = 1 / (1 + invQ * n + nSquared); const auto c1 = 1 / (1 + invQ * n + nSquared);
return { { c1, c1 * -2, c1, return { { c1, c1 * -2, c1,
1, c1 * 2 * (nSquared - 1), 1, c1 * 2 * (nSquared - 1),
@ -134,10 +136,10 @@ std::array<NumericType, 6> ArrayCoefficients<NumericType>::makeBandPass (double
jassert (frequency > 0 && frequency <= static_cast<float> (sampleRate * 0.5)); jassert (frequency > 0 && frequency <= static_cast<float> (sampleRate * 0.5));
jassert (Q > 0.0); jassert (Q > 0.0);
auto n = 1 / std::tan (MathConstants<NumericType>::pi * frequency / static_cast<NumericType> (sampleRate)); const auto n = 1 / std::tan (MathConstants<NumericType>::pi * frequency / static_cast<NumericType> (sampleRate));
auto nSquared = n * n; const auto nSquared = n * n;
auto invQ = 1 / Q; const auto invQ = 1 / Q;
auto c1 = 1 / (1 + invQ * n + nSquared); const auto c1 = 1 / (1 + invQ * n + nSquared);
return { { c1 * n * invQ, 0, return { { c1 * n * invQ, 0,
-c1 * n * invQ, 1, -c1 * n * invQ, 1,
@ -161,12 +163,12 @@ std::array<NumericType, 6> ArrayCoefficients<NumericType>::makeNotch (double sam
jassert (frequency > 0 && frequency <= static_cast<float> (sampleRate * 0.5)); jassert (frequency > 0 && frequency <= static_cast<float> (sampleRate * 0.5));
jassert (Q > 0.0); jassert (Q > 0.0);
auto n = 1 / std::tan (MathConstants<NumericType>::pi * frequency / static_cast<NumericType> (sampleRate)); const auto n = 1 / std::tan (MathConstants<NumericType>::pi * frequency / static_cast<NumericType> (sampleRate));
auto nSquared = n * n; const auto nSquared = n * n;
auto invQ = 1 / Q; const auto invQ = 1 / Q;
auto c1 = 1 / (1 + n * invQ + nSquared); const auto c1 = 1 / (1 + n * invQ + nSquared);
auto b0 = c1 * (1 + nSquared); const auto b0 = c1 * (1 + nSquared);
auto b1 = 2 * c1 * (1 - nSquared); const auto b1 = 2 * c1 * (1 - nSquared);
return { { b0, b1, b0, 1, b1, c1 * (1 - n * invQ + nSquared) } }; return { { b0, b1, b0, 1, b1, c1 * (1 - n * invQ + nSquared) } };
} }
@ -187,12 +189,12 @@ std::array<NumericType, 6> ArrayCoefficients<NumericType>::makeAllPass (double s
jassert (frequency > 0 && frequency <= sampleRate * 0.5); jassert (frequency > 0 && frequency <= sampleRate * 0.5);
jassert (Q > 0); jassert (Q > 0);
auto n = 1 / std::tan (MathConstants<NumericType>::pi * frequency / static_cast<NumericType> (sampleRate)); const auto n = 1 / std::tan (MathConstants<NumericType>::pi * frequency / static_cast<NumericType> (sampleRate));
auto nSquared = n * n; const auto nSquared = n * n;
auto invQ = 1 / Q; const auto invQ = 1 / Q;
auto c1 = 1 / (1 + invQ * n + nSquared); const auto c1 = 1 / (1 + invQ * n + nSquared);
auto b0 = c1 * (1 - n * invQ + nSquared); const auto b0 = c1 * (1 - n * invQ + nSquared);
auto b1 = c1 * 2 * (1 - nSquared); const auto b1 = c1 * 2 * (1 - nSquared);
return { { b0, b1, 1, 1, b1, b0 } }; return { { b0, b1, 1, 1, b1, b0 } };
} }
@ -207,13 +209,13 @@ std::array<NumericType, 6> ArrayCoefficients<NumericType>::makeLowShelf (double
jassert (cutOffFrequency > 0.0 && cutOffFrequency <= sampleRate * 0.5); jassert (cutOffFrequency > 0.0 && cutOffFrequency <= sampleRate * 0.5);
jassert (Q > 0.0); jassert (Q > 0.0);
auto A = jmax (static_cast<NumericType> (0.0), std::sqrt (gainFactor)); const auto A = std::sqrt (Decibels::gainWithLowerBound (gainFactor, (NumericType) minimumDecibels));
auto aminus1 = A - 1; const auto aminus1 = A - 1;
auto aplus1 = A + 1; const auto aplus1 = A + 1;
auto omega = (2 * MathConstants<NumericType>::pi * jmax (cutOffFrequency, static_cast<NumericType> (2.0))) / static_cast<NumericType> (sampleRate); const auto omega = (2 * MathConstants<NumericType>::pi * jmax (cutOffFrequency, static_cast<NumericType> (2.0))) / static_cast<NumericType> (sampleRate);
auto coso = std::cos (omega); const auto coso = std::cos (omega);
auto beta = std::sin (omega) * std::sqrt (A) / Q; const auto beta = std::sin (omega) * std::sqrt (A) / Q;
auto aminus1TimesCoso = aminus1 * coso; const auto aminus1TimesCoso = aminus1 * coso;
return { { A * (aplus1 - aminus1TimesCoso + beta), return { { A * (aplus1 - aminus1TimesCoso + beta),
A * 2 * (aminus1 - aplus1 * coso), A * 2 * (aminus1 - aplus1 * coso),
@ -233,13 +235,13 @@ std::array<NumericType, 6> ArrayCoefficients<NumericType>::makeHighShelf (double
jassert (cutOffFrequency > 0 && cutOffFrequency <= static_cast<NumericType> (sampleRate * 0.5)); jassert (cutOffFrequency > 0 && cutOffFrequency <= static_cast<NumericType> (sampleRate * 0.5));
jassert (Q > 0); jassert (Q > 0);
auto A = jmax (static_cast<NumericType> (0.0), std::sqrt (gainFactor)); const auto A = std::sqrt (Decibels::gainWithLowerBound (gainFactor, (NumericType) minimumDecibels));
auto aminus1 = A - 1; const auto aminus1 = A - 1;
auto aplus1 = A + 1; const auto aplus1 = A + 1;
auto omega = (2 * MathConstants<NumericType>::pi * jmax (cutOffFrequency, static_cast<NumericType> (2.0))) / static_cast<NumericType> (sampleRate); const auto omega = (2 * MathConstants<NumericType>::pi * jmax (cutOffFrequency, static_cast<NumericType> (2.0))) / static_cast<NumericType> (sampleRate);
auto coso = std::cos (omega); const auto coso = std::cos (omega);
auto beta = std::sin (omega) * std::sqrt (A) / Q; const auto beta = std::sin (omega) * std::sqrt (A) / Q;
auto aminus1TimesCoso = aminus1 * coso; const auto aminus1TimesCoso = aminus1 * coso;
return { { A * (aplus1 + aminus1TimesCoso + beta), return { { A * (aplus1 + aminus1TimesCoso + beta),
A * -2 * (aminus1 + aplus1 * coso), A * -2 * (aminus1 + aplus1 * coso),
@ -260,12 +262,12 @@ std::array<NumericType, 6> ArrayCoefficients<NumericType>::makePeakFilter (doubl
jassert (Q > 0); jassert (Q > 0);
jassert (gainFactor > 0); jassert (gainFactor > 0);
auto A = jmax (static_cast<NumericType> (0.0), std::sqrt (gainFactor)); const auto A = std::sqrt (Decibels::gainWithLowerBound (gainFactor, (NumericType) minimumDecibels));
auto omega = (2 * MathConstants<NumericType>::pi * jmax (frequency, static_cast<NumericType> (2.0))) / static_cast<NumericType> (sampleRate); const auto omega = (2 * MathConstants<NumericType>::pi * jmax (frequency, static_cast<NumericType> (2.0))) / static_cast<NumericType> (sampleRate);
auto alpha = std::sin (omega) / (Q * 2); const auto alpha = std::sin (omega) / (Q * 2);
auto c2 = -2 * std::cos (omega); const auto c2 = -2 * std::cos (omega);
auto alphaTimesA = alpha * A; const auto alphaTimesA = alpha * A;
auto alphaOverA = alpha / A; const auto alphaOverA = alpha / A;
return { { 1 + alphaTimesA, c2, 1 - alphaTimesA, 1 + alphaOverA, c2, 1 - alphaOverA } }; return { { 1 + alphaTimesA, c2, 1 - alphaTimesA, 1 + alphaOverA, c2, 1 - alphaOverA } };
} }