fix macOS build (following Projucer changes made in Windows, which removed /Applications/JUCE/modules from its headers). move JUCE headers under source control, so that Windows and macOS can both build against same version of JUCE. remove AUv3 target (I think it's an iOS thing, so it will never work with this macOS fluidsynth dylib).

This commit is contained in:
Alex Birch
2018-06-17 13:34:53 +01:00
parent a2be47c887
commit dff4d13a1d
1563 changed files with 601601 additions and 3466 deletions

View File

@ -0,0 +1,75 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
struct CatmullRomAlgorithm
{
static forcedinline float valueAtOffset (const float* const inputs, const float offset) noexcept
{
auto y0 = inputs[3];
auto y1 = inputs[2];
auto y2 = inputs[1];
auto y3 = inputs[0];
auto halfY0 = 0.5f * y0;
auto halfY3 = 0.5f * y3;
return y1 + offset * ((0.5f * y2 - halfY0)
+ (offset * (((y0 + 2.0f * y2) - (halfY3 + 2.5f * y1))
+ (offset * ((halfY3 + 1.5f * y1) - (halfY0 + 1.5f * y2))))));
}
};
CatmullRomInterpolator::CatmullRomInterpolator() noexcept { reset(); }
CatmullRomInterpolator::~CatmullRomInterpolator() noexcept {}
void CatmullRomInterpolator::reset() noexcept
{
subSamplePos = 1.0;
for (auto& s : lastInputSamples)
s = 0;
}
int CatmullRomInterpolator::process (double actualRatio, const float* in, float* out, int numOut, int available, int wrap) noexcept
{
return interpolate<CatmullRomAlgorithm> (lastInputSamples, subSamplePos, actualRatio, in, out, numOut, available, wrap);
}
int CatmullRomInterpolator::process (double actualRatio, const float* in, float* out, int numOut) noexcept
{
return interpolate<CatmullRomAlgorithm> (lastInputSamples, subSamplePos, actualRatio, in, out, numOut);
}
int CatmullRomInterpolator::processAdding (double actualRatio, const float* in, float* out, int numOut, int available, int wrap, float gain) noexcept
{
return interpolateAdding<CatmullRomAlgorithm> (lastInputSamples, subSamplePos, actualRatio, in, out, numOut, available, wrap, gain);
}
int CatmullRomInterpolator::processAdding (double actualRatio, const float* in, float* out, int numOut, float gain) noexcept
{
return interpolateAdding<CatmullRomAlgorithm> (lastInputSamples, subSamplePos, actualRatio, in, out, numOut, gain);
}
} // namespace juce

View File

@ -0,0 +1,143 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
/**
Interpolator for resampling a stream of floats using Catmull-Rom interpolation.
Note that the resampler is stateful, so when there's a break in the continuity
of the input stream you're feeding it, you should call reset() before feeding
it any new data. And like with any other stateful filter, if you're resampling
multiple channels, make sure each one uses its own CatmullRomInterpolator
object.
@see LagrangeInterpolator
@tags{Audio}
*/
class JUCE_API CatmullRomInterpolator
{
public:
CatmullRomInterpolator() noexcept;
~CatmullRomInterpolator() noexcept;
/** Resets the state of the interpolator.
Call this when there's a break in the continuity of the input data stream.
*/
void reset() noexcept;
/** Resamples a stream of samples.
@param speedRatio the number of input samples to use for each output sample
@param inputSamples the source data to read from. This must contain at
least (speedRatio * numOutputSamplesToProduce) samples.
@param outputSamples the buffer to write the results into
@param numOutputSamplesToProduce the number of output samples that should be created
@returns the actual number of input samples that were used
*/
int process (double speedRatio,
const float* inputSamples,
float* outputSamples,
int numOutputSamplesToProduce) noexcept;
/** Resamples a stream of samples.
@param speedRatio the number of input samples to use for each output sample
@param inputSamples the source data to read from. This must contain at
least (speedRatio * numOutputSamplesToProduce) samples.
@param outputSamples the buffer to write the results into
@param numOutputSamplesToProduce the number of output samples that should be created
@param available the number of available input samples. If it needs more samples
than available, it either wraps back for wrapAround samples, or
it feeds zeroes
@param wrapAround if the stream exceeds available samples, it wraps back for
wrapAround samples. If wrapAround is set to 0, it will feed zeroes.
@returns the actual number of input samples that were used
*/
int process (double speedRatio,
const float* inputSamples,
float* outputSamples,
int numOutputSamplesToProduce,
int available,
int wrapAround) noexcept;
/** Resamples a stream of samples, adding the results to the output data
with a gain.
@param speedRatio the number of input samples to use for each output sample
@param inputSamples the source data to read from. This must contain at
least (speedRatio * numOutputSamplesToProduce) samples.
@param outputSamples the buffer to write the results to - the result values will be added
to any pre-existing data in this buffer after being multiplied by
the gain factor
@param numOutputSamplesToProduce the number of output samples that should be created
@param gain a gain factor to multiply the resulting samples by before
adding them to the destination buffer
@returns the actual number of input samples that were used
*/
int processAdding (double speedRatio,
const float* inputSamples,
float* outputSamples,
int numOutputSamplesToProduce,
float gain) noexcept;
/** Resamples a stream of samples, adding the results to the output data
with a gain.
@param speedRatio the number of input samples to use for each output sample
@param inputSamples the source data to read from. This must contain at
least (speedRatio * numOutputSamplesToProduce) samples.
@param outputSamples the buffer to write the results to - the result values will be added
to any pre-existing data in this buffer after being multiplied by
the gain factor
@param numOutputSamplesToProduce the number of output samples that should be created
@param available the number of available input samples. If it needs more samples
than available, it either wraps back for wrapAround samples, or
it feeds zeroes
@param wrapAround if the stream exceeds available samples, it wraps back for
wrapAround samples. If wrapAround is set to 0, it will feed zeroes.
@param gain a gain factor to multiply the resulting samples by before
adding them to the destination buffer
@returns the actual number of input samples that were used
*/
int processAdding (double speedRatio,
const float* inputSamples,
float* outputSamples,
int numOutputSamplesToProduce,
int available,
int wrapAround,
float gain) noexcept;
private:
float lastInputSamples[5];
double subSamplePos;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CatmullRomInterpolator)
};
} // namespace juce

View File

@ -0,0 +1,112 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
This class contains some helpful static methods for dealing with decibel values.
@tags{Audio}
*/
class Decibels
{
public:
//==============================================================================
/** Converts a dBFS value to its equivalent gain level.
A gain of 1.0 = 0 dB, and lower gains map onto negative decibel values. Any
decibel value lower than minusInfinityDb will return a gain of 0.
*/
template <typename Type>
static Type decibelsToGain (Type decibels,
Type minusInfinityDb = Type (defaultMinusInfinitydB))
{
return decibels > minusInfinityDb ? std::pow (Type (10.0), decibels * Type (0.05))
: Type();
}
/** Converts a gain level into a dBFS value.
A gain of 1.0 = 0 dB, and lower gains map onto negative decibel values.
If the gain is 0 (or negative), then the method will return the value
provided as minusInfinityDb.
*/
template <typename Type>
static Type gainToDecibels (Type gain,
Type minusInfinityDb = Type (defaultMinusInfinitydB))
{
return gain > Type() ? jmax (minusInfinityDb, static_cast<Type> (std::log10 (gain)) * Type (20.0))
: minusInfinityDb;
}
//==============================================================================
/** Converts a decibel reading to a string.
By default the returned string will have the 'dB' suffix added, but this can be removed by
setting the shouldIncludeSuffix argument to false. If a customMinusInfinityString argument
is provided this will be returned if the value is lower than minusInfinityDb, otherwise
the return value will be "-INF".
*/
template <typename Type>
static String toString (Type decibels,
int decimalPlaces = 2,
Type minusInfinityDb = Type (defaultMinusInfinitydB),
bool shouldIncludeSuffix = true,
StringRef customMinusInfinityString = {})
{
String s;
s.preallocateBytes (20);
if (decibels <= minusInfinityDb)
{
if (customMinusInfinityString.isEmpty())
s << "-INF";
else
s << customMinusInfinityString;
}
else
{
if (decibels >= Type())
s << '+';
if (decimalPlaces <= 0)
s << roundToInt (decibels);
else
s << String (decibels, decimalPlaces);
}
if (shouldIncludeSuffix)
s << " dB";
return s;
}
private:
//==============================================================================
enum { defaultMinusInfinitydB = -100 };
Decibels() = delete; // This class can't be instantiated, it's just a holder for static methods..
};
} // namespace juce

View File

@ -0,0 +1,336 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
IIRCoefficients::IIRCoefficients() noexcept
{
zeromem (coefficients, sizeof (coefficients));
}
IIRCoefficients::~IIRCoefficients() noexcept {}
IIRCoefficients::IIRCoefficients (const IIRCoefficients& other) noexcept
{
memcpy (coefficients, other.coefficients, sizeof (coefficients));
}
IIRCoefficients& IIRCoefficients::operator= (const IIRCoefficients& other) noexcept
{
memcpy (coefficients, other.coefficients, sizeof (coefficients));
return *this;
}
IIRCoefficients::IIRCoefficients (double c1, double c2, double c3,
double c4, double c5, double c6) noexcept
{
auto a = 1.0 / c4;
coefficients[0] = (float) (c1 * a);
coefficients[1] = (float) (c2 * a);
coefficients[2] = (float) (c3 * a);
coefficients[3] = (float) (c5 * a);
coefficients[4] = (float) (c6 * a);
}
IIRCoefficients IIRCoefficients::makeLowPass (double sampleRate,
double frequency) noexcept
{
return makeLowPass (sampleRate, frequency, 1.0 / MathConstants<double>::sqrt2);
}
IIRCoefficients IIRCoefficients::makeLowPass (double sampleRate,
double frequency,
double Q) noexcept
{
jassert (sampleRate > 0.0);
jassert (frequency > 0.0 && frequency <= sampleRate * 0.5);
jassert (Q > 0.0);
auto n = 1.0 / std::tan (MathConstants<double>::pi * frequency / sampleRate);
auto nSquared = n * n;
auto c1 = 1.0 / (1.0 + 1.0 / Q * n + nSquared);
return IIRCoefficients (c1,
c1 * 2.0,
c1,
1.0,
c1 * 2.0 * (1.0 - nSquared),
c1 * (1.0 - 1.0 / Q * n + nSquared));
}
IIRCoefficients IIRCoefficients::makeHighPass (double sampleRate,
double frequency) noexcept
{
return makeHighPass (sampleRate, frequency, 1.0 / std::sqrt(2.0));
}
IIRCoefficients IIRCoefficients::makeHighPass (double sampleRate,
double frequency,
double Q) noexcept
{
jassert (sampleRate > 0.0);
jassert (frequency > 0.0 && frequency <= sampleRate * 0.5);
jassert (Q > 0.0);
auto n = std::tan (MathConstants<double>::pi * frequency / sampleRate);
auto nSquared = n * n;
auto c1 = 1.0 / (1.0 + 1.0 / Q * n + nSquared);
return IIRCoefficients (c1,
c1 * -2.0,
c1,
1.0,
c1 * 2.0 * (nSquared - 1.0),
c1 * (1.0 - 1.0 / Q * n + nSquared));
}
IIRCoefficients IIRCoefficients::makeBandPass (double sampleRate,
double frequency) noexcept
{
return makeBandPass (sampleRate, frequency, 1.0 / MathConstants<double>::sqrt2);
}
IIRCoefficients IIRCoefficients::makeBandPass (double sampleRate,
double frequency,
double Q) noexcept
{
jassert (sampleRate > 0.0);
jassert (frequency > 0.0 && frequency <= sampleRate * 0.5);
jassert (Q > 0.0);
auto n = 1.0 / std::tan (MathConstants<double>::pi * frequency / sampleRate);
auto nSquared = n * n;
auto c1 = 1.0 / (1.0 + 1.0 / Q * n + nSquared);
return IIRCoefficients (c1 * n / Q,
0.0,
-c1 * n / Q,
1.0,
c1 * 2.0 * (1.0 - nSquared),
c1 * (1.0 - 1.0 / Q * n + nSquared));
}
IIRCoefficients IIRCoefficients::makeNotchFilter (double sampleRate,
double frequency) noexcept
{
return makeNotchFilter (sampleRate, frequency, 1.0 / MathConstants<double>::sqrt2);
}
IIRCoefficients IIRCoefficients::makeNotchFilter (double sampleRate,
double frequency,
double Q) noexcept
{
jassert (sampleRate > 0.0);
jassert (frequency > 0.0 && frequency <= sampleRate * 0.5);
jassert (Q > 0.0);
auto n = 1.0 / std::tan (MathConstants<double>::pi * frequency / sampleRate);
auto nSquared = n * n;
auto c1 = 1.0 / (1.0 + n / Q + nSquared);
return IIRCoefficients (c1 * (1.0 + nSquared),
2.0 * c1 * (1.0 - nSquared),
c1 * (1.0 + nSquared),
1.0,
c1 * 2.0 * (1.0 - nSquared),
c1 * (1.0 - n / Q + nSquared));
}
IIRCoefficients IIRCoefficients::makeAllPass (double sampleRate,
double frequency) noexcept
{
return makeAllPass (sampleRate, frequency, 1.0 / MathConstants<double>::sqrt2);
}
IIRCoefficients IIRCoefficients::makeAllPass (double sampleRate,
double frequency,
double Q) noexcept
{
jassert (sampleRate > 0.0);
jassert (frequency > 0.0 && frequency <= sampleRate * 0.5);
jassert (Q > 0.0);
auto n = 1.0 / std::tan (MathConstants<double>::pi * frequency / sampleRate);
auto nSquared = n * n;
auto c1 = 1.0 / (1.0 + 1.0 / Q * n + nSquared);
return IIRCoefficients (c1 * (1.0 - n / Q + nSquared),
c1 * 2.0 * (1.0 - nSquared),
1.0,
1.0,
c1 * 2.0 * (1.0 - nSquared),
c1 * (1.0 - n / Q + nSquared));
}
IIRCoefficients IIRCoefficients::makeLowShelf (double sampleRate,
double cutOffFrequency,
double Q,
float gainFactor) noexcept
{
jassert (sampleRate > 0.0);
jassert (cutOffFrequency > 0.0 && cutOffFrequency <= sampleRate * 0.5);
jassert (Q > 0.0);
auto A = jmax (0.0f, std::sqrt (gainFactor));
auto aminus1 = A - 1.0;
auto aplus1 = A + 1.0;
auto omega = (MathConstants<double>::twoPi * jmax (cutOffFrequency, 2.0)) / sampleRate;
auto coso = std::cos (omega);
auto beta = std::sin (omega) * std::sqrt (A) / Q;
auto aminus1TimesCoso = aminus1 * coso;
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);
}
IIRCoefficients IIRCoefficients::makeHighShelf (double sampleRate,
double cutOffFrequency,
double Q,
float gainFactor) noexcept
{
jassert (sampleRate > 0.0);
jassert (cutOffFrequency > 0.0 && cutOffFrequency <= sampleRate * 0.5);
jassert (Q > 0.0);
auto A = jmax (0.0f, std::sqrt (gainFactor));
auto aminus1 = A - 1.0;
auto aplus1 = A + 1.0;
auto omega = (MathConstants<double>::twoPi * jmax (cutOffFrequency, 2.0)) / sampleRate;
auto coso = std::cos (omega);
auto beta = std::sin (omega) * std::sqrt (A) / Q;
auto aminus1TimesCoso = aminus1 * coso;
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);
}
IIRCoefficients IIRCoefficients::makePeakFilter (double sampleRate,
double frequency,
double Q,
float gainFactor) noexcept
{
jassert (sampleRate > 0.0);
jassert (frequency > 0.0 && frequency <= sampleRate * 0.5);
jassert (Q > 0.0);
auto A = jmax (0.0f, std::sqrt (gainFactor));
auto omega = (MathConstants<double>::twoPi * jmax (frequency, 2.0)) / sampleRate;
auto alpha = 0.5 * std::sin (omega) / Q;
auto c2 = -2.0 * std::cos (omega);
auto alphaTimesA = alpha * A;
auto alphaOverA = alpha / A;
return IIRCoefficients (1.0 + alphaTimesA,
c2,
1.0 - alphaTimesA,
1.0 + alphaOverA,
c2,
1.0 - alphaOverA);
}
//==============================================================================
IIRFilter::IIRFilter() noexcept
{
}
IIRFilter::IIRFilter (const IIRFilter& other) noexcept : active (other.active)
{
const SpinLock::ScopedLockType sl (other.processLock);
coefficients = other.coefficients;
}
IIRFilter::~IIRFilter() noexcept
{
}
//==============================================================================
void IIRFilter::makeInactive() noexcept
{
const SpinLock::ScopedLockType sl (processLock);
active = false;
}
void IIRFilter::setCoefficients (const IIRCoefficients& newCoefficients) noexcept
{
const SpinLock::ScopedLockType sl (processLock);
coefficients = newCoefficients;
active = true;
}
//==============================================================================
void IIRFilter::reset() noexcept
{
const SpinLock::ScopedLockType sl (processLock);
v1 = v2 = 0.0;
}
float IIRFilter::processSingleSampleRaw (float in) noexcept
{
auto out = coefficients.coefficients[0] * in + v1;
JUCE_SNAP_TO_ZERO (out);
v1 = coefficients.coefficients[1] * in - coefficients.coefficients[3] * out + v2;
v2 = coefficients.coefficients[2] * in - coefficients.coefficients[4] * out;
return out;
}
void IIRFilter::processSamples (float* const samples, const int numSamples) noexcept
{
const SpinLock::ScopedLockType sl (processLock);
if (active)
{
auto c0 = coefficients.coefficients[0];
auto c1 = coefficients.coefficients[1];
auto c2 = coefficients.coefficients[2];
auto c3 = coefficients.coefficients[3];
auto c4 = coefficients.coefficients[4];
auto lv1 = v1, lv2 = v2;
for (int i = 0; i < numSamples; ++i)
{
auto in = samples[i];
auto 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;
}
}
} // namespace juce

View File

@ -0,0 +1,217 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
class IIRFilter;
//==============================================================================
/**
A set of coefficients for use in an IIRFilter object.
@see IIRFilter
@tags{Audio}
*/
class JUCE_API IIRCoefficients
{
public:
//==============================================================================
/** Creates a null set of coefficients (which will produce silence). */
IIRCoefficients() noexcept;
/** Directly constructs an object from the raw coefficients.
Most people will want to use the static methods instead of this, but
the constructor is public to allow tinkerers to create their own custom
filters!
*/
IIRCoefficients (double c1, double c2, double c3,
double c4, double c5, double c6) 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 low-pass filter with variable Q. */
static IIRCoefficients makeLowPass (double sampleRate,
double frequency,
double Q) noexcept;
//==============================================================================
/** Returns the coefficients for a high-pass filter. */
static IIRCoefficients makeHighPass (double sampleRate,
double frequency) noexcept;
/** Returns the coefficients for a high-pass filter with variable Q. */
static IIRCoefficients makeHighPass (double sampleRate,
double frequency,
double Q) noexcept;
//==============================================================================
/** Returns the coefficients for a band-pass filter. */
static IIRCoefficients makeBandPass (double sampleRate, double frequency) noexcept;
/** Returns the coefficients for a band-pass filter with variable Q. */
static IIRCoefficients makeBandPass (double sampleRate,
double frequency,
double Q) noexcept;
//==============================================================================
/** Returns the coefficients for a notch filter. */
static IIRCoefficients makeNotchFilter (double sampleRate, double frequency) noexcept;
/** Returns the coefficients for a notch filter with variable Q. */
static IIRCoefficients makeNotchFilter (double sampleRate,
double frequency,
double Q) noexcept;
//==============================================================================
/** Returns the coefficients for an all-pass filter. */
static IIRCoefficients makeAllPass (double sampleRate, double frequency) noexcept;
/** Returns the coefficients for an all-pass filter with variable Q. */
static IIRCoefficients makeAllPass (double sampleRate,
double frequency,
double Q) 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;
//==============================================================================
/** The raw coefficients.
You should leave these numbers alone unless you really know what you're doing.
*/
float coefficients[5];
};
//==============================================================================
/**
An IIR filter that can perform low, high, or band-pass filtering on an
audio signal.
@see IIRCoefficient, IIRFilterAudioSource
@tags{Audio}
*/
class JUCE_API IIRFilter
{
public:
//==============================================================================
/** Creates a filter.
Initially the filter is inactive, so will have no effect on samples that
you process with it. Use the setCoefficients() method to turn it into the
type of filter needed.
*/
IIRFilter() noexcept;
/** Creates a copy of another filter. */
IIRFilter (const IIRFilter&) noexcept;
/** Destructor. */
~IIRFilter() noexcept;
//==============================================================================
/** 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 noexcept { return coefficients; }
//==============================================================================
/** Resets the filter's processing pipeline, ready to start a new stream of data.
Note that this clears the processing state, but the type of filter and
its coefficients aren't changed. To put a filter into an inactive state, use
the makeInactive() method.
*/
void reset() 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.
Use this if you need fast processing of a single value, but be aware that
this isn't thread-safe in the way that processSamples() is.
*/
float processSingleSampleRaw (float sample) noexcept;
protected:
//==============================================================================
SpinLock processLock;
IIRCoefficients coefficients;
float v1 = 0, v2 = 0;
bool active = false;
// The exact meaning of an assignment operator would be ambiguous since the filters are
// stateful. If you want to copy the coefficients, then just use setCoefficients().
IIRFilter& operator= (const IIRFilter&) = delete;
JUCE_LEAK_DETECTOR (IIRFilter)
};
} // namespace juce

View File

@ -0,0 +1,459 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
namespace
{
static forcedinline void pushInterpolationSample (float* lastInputSamples, float newValue) noexcept
{
lastInputSamples[4] = lastInputSamples[3];
lastInputSamples[3] = lastInputSamples[2];
lastInputSamples[2] = lastInputSamples[1];
lastInputSamples[1] = lastInputSamples[0];
lastInputSamples[0] = newValue;
}
static forcedinline void pushInterpolationSamples (float* lastInputSamples, const float* input, int numOut) noexcept
{
if (numOut >= 5)
{
for (int i = 0; i < 5; ++i)
lastInputSamples[i] = input[--numOut];
}
else
{
for (int i = 0; i < numOut; ++i)
pushInterpolationSample (lastInputSamples, input[i]);
}
}
static forcedinline void pushInterpolationSamples (float* lastInputSamples, const float* input,
int numOut, int available, int wrapAround) noexcept
{
if (numOut >= 5)
{
if (available >= 5)
{
for (int i = 0; i < 5; ++i)
lastInputSamples[i] = input[--numOut];
}
else
{
for (int i = 0; i < available; ++i)
lastInputSamples[i] = input[--numOut];
if (wrapAround > 0)
{
numOut -= wrapAround;
for (int i = available; i < 5; ++i)
lastInputSamples[i] = input[--numOut];
}
else
{
for (int i = available; i < 5; ++i)
lastInputSamples[i] = 0.0f;
}
}
}
else
{
if (numOut > available)
{
for (int i = 0; i < available; ++i)
pushInterpolationSample (lastInputSamples, input[i]);
if (wrapAround > 0)
{
for (int i = 0; i < numOut - available; ++i)
pushInterpolationSample (lastInputSamples, input[i + available - wrapAround]);
}
else
{
for (int i = 0; i < numOut - available; ++i)
pushInterpolationSample (lastInputSamples, 0);
}
}
else
{
for (int i = 0; i < numOut; ++i)
pushInterpolationSample (lastInputSamples, input[i]);
}
}
}
template <typename InterpolatorType>
static int interpolate (float* lastInputSamples, double& subSamplePos, double actualRatio,
const float* in, float* out, int numOut) noexcept
{
auto pos = subSamplePos;
if (actualRatio == 1.0 && pos == 1.0)
{
memcpy (out, in, (size_t) numOut * sizeof (float));
pushInterpolationSamples (lastInputSamples, in, numOut);
return numOut;
}
int numUsed = 0;
while (numOut > 0)
{
while (pos >= 1.0)
{
pushInterpolationSample (lastInputSamples, in[numUsed++]);
pos -= 1.0;
}
*out++ = InterpolatorType::valueAtOffset (lastInputSamples, (float) pos);
pos += actualRatio;
--numOut;
}
subSamplePos = pos;
return numUsed;
}
template <typename InterpolatorType>
static int interpolate (float* lastInputSamples, double& subSamplePos, double actualRatio,
const float* in, float* out, int numOut, int available, int wrap) noexcept
{
if (actualRatio == 1.0)
{
if (available >= numOut)
{
memcpy (out, in, (size_t) numOut * sizeof (float));
pushInterpolationSamples (lastInputSamples, in, numOut, available, wrap);
}
else
{
memcpy (out, in, (size_t) available * sizeof (float));
pushInterpolationSamples (lastInputSamples, in, numOut, available, wrap);
if (wrap > 0)
{
memcpy (out + available, in + available - wrap, (size_t) (numOut - available) * sizeof (float));
pushInterpolationSamples (lastInputSamples, in, numOut, available, wrap);
}
else
{
for (int i = 0; i < numOut - available; ++i)
pushInterpolationSample (lastInputSamples, 0);
}
}
return numOut;
}
auto originalIn = in;
auto pos = subSamplePos;
bool exceeded = false;
if (actualRatio < 1.0)
{
for (int i = numOut; --i >= 0;)
{
if (pos >= 1.0)
{
if (exceeded)
{
pushInterpolationSample (lastInputSamples, 0);
}
else
{
pushInterpolationSample (lastInputSamples, *in++);
if (--available <= 0)
{
if (wrap > 0)
{
in -= wrap;
available += wrap;
}
else
{
exceeded = true;
}
}
}
pos -= 1.0;
}
*out++ = InterpolatorType::valueAtOffset (lastInputSamples, (float) pos);
pos += actualRatio;
}
}
else
{
for (int i = numOut; --i >= 0;)
{
while (pos < actualRatio)
{
if (exceeded)
{
pushInterpolationSample (lastInputSamples, 0);
}
else
{
pushInterpolationSample (lastInputSamples, *in++);
if (--available <= 0)
{
if (wrap > 0)
{
in -= wrap;
available += wrap;
}
else
{
exceeded = true;
}
}
}
pos += 1.0;
}
pos -= actualRatio;
*out++ = InterpolatorType::valueAtOffset (lastInputSamples, jmax (0.0f, 1.0f - (float) pos));
}
}
subSamplePos = pos;
return ((int) (in - originalIn) + wrap) % wrap;
}
template <typename InterpolatorType>
static int interpolateAdding (float* lastInputSamples, double& subSamplePos, double actualRatio,
const float* in, float* out, int numOut,
int available, int wrap, float gain) noexcept
{
if (actualRatio == 1.0)
{
if (available >= numOut)
{
FloatVectorOperations::addWithMultiply (out, in, gain, numOut);
pushInterpolationSamples (lastInputSamples, in, numOut, available, wrap);
}
else
{
FloatVectorOperations::addWithMultiply (out, in, gain, available);
pushInterpolationSamples (lastInputSamples, in, available, available, wrap);
if (wrap > 0)
{
FloatVectorOperations::addWithMultiply (out, in - wrap, gain, numOut - available);
pushInterpolationSamples (lastInputSamples, in - wrap, numOut - available, available, wrap);
}
else
{
for (int i = 0; i < numOut-available; ++i)
pushInterpolationSample (lastInputSamples, 0.0);
}
}
return numOut;
}
auto originalIn = in;
auto pos = subSamplePos;
bool exceeded = false;
if (actualRatio < 1.0)
{
for (int i = numOut; --i >= 0;)
{
if (pos >= 1.0)
{
if (exceeded)
{
pushInterpolationSample (lastInputSamples, 0.0);
}
else
{
pushInterpolationSample (lastInputSamples, *in++);
if (--available <= 0)
{
if (wrap > 0)
{
in -= wrap;
available += wrap;
}
else
{
exceeded = true;
}
}
}
pos -= 1.0;
}
*out++ += gain * InterpolatorType::valueAtOffset (lastInputSamples, (float) pos);
pos += actualRatio;
}
}
else
{
for (int i = numOut; --i >= 0;)
{
while (pos < actualRatio)
{
if (exceeded)
{
pushInterpolationSample (lastInputSamples, 0.0);
}
else
{
pushInterpolationSample (lastInputSamples, *in++);
if (--available <= 0)
{
if (wrap > 0)
{
in -= wrap;
available += wrap;
}
else
{
exceeded = true;
}
}
}
pos += 1.0;
}
pos -= actualRatio;
*out++ += gain * InterpolatorType::valueAtOffset (lastInputSamples, jmax (0.0f, 1.0f - (float) pos));
}
}
subSamplePos = pos;
return ((int) (in - originalIn) + wrap) % wrap;
}
template <typename InterpolatorType>
static int interpolateAdding (float* lastInputSamples, double& subSamplePos, double actualRatio,
const float* in, float* out, int numOut, float gain) noexcept
{
auto pos = subSamplePos;
if (actualRatio == 1.0 && pos == 1.0)
{
FloatVectorOperations::addWithMultiply (out, in, gain, numOut);
pushInterpolationSamples (lastInputSamples, in, numOut);
return numOut;
}
int numUsed = 0;
while (numOut > 0)
{
while (pos >= 1.0)
{
pushInterpolationSample (lastInputSamples, in[numUsed++]);
pos -= 1.0;
}
*out++ += gain * InterpolatorType::valueAtOffset (lastInputSamples, (float) pos);
pos += actualRatio;
--numOut;
}
subSamplePos = pos;
return numUsed;
}
}
//==============================================================================
template <int k>
struct LagrangeResampleHelper
{
static forcedinline void calc (float& a, float b) noexcept { a *= b * (1.0f / k); }
};
template<>
struct LagrangeResampleHelper<0>
{
static forcedinline void calc (float&, float) noexcept {}
};
struct LagrangeAlgorithm
{
static forcedinline float valueAtOffset (const float* inputs, float offset) noexcept
{
return calcCoefficient<0> (inputs[4], offset)
+ calcCoefficient<1> (inputs[3], offset)
+ calcCoefficient<2> (inputs[2], offset)
+ calcCoefficient<3> (inputs[1], offset)
+ calcCoefficient<4> (inputs[0], offset);
}
template <int k>
static forcedinline float calcCoefficient (float input, float offset) noexcept
{
LagrangeResampleHelper<0 - k>::calc (input, -2.0f - offset);
LagrangeResampleHelper<1 - k>::calc (input, -1.0f - offset);
LagrangeResampleHelper<2 - k>::calc (input, 0.0f - offset);
LagrangeResampleHelper<3 - k>::calc (input, 1.0f - offset);
LagrangeResampleHelper<4 - k>::calc (input, 2.0f - offset);
return input;
}
};
LagrangeInterpolator::LagrangeInterpolator() noexcept { reset(); }
LagrangeInterpolator::~LagrangeInterpolator() noexcept {}
void LagrangeInterpolator::reset() noexcept
{
subSamplePos = 1.0;
for (auto& s : lastInputSamples)
s = 0;
}
int LagrangeInterpolator::process (double actualRatio, const float* in, float* out, int numOut, int available, int wrap) noexcept
{
return interpolate<LagrangeAlgorithm> (lastInputSamples, subSamplePos, actualRatio, in, out, numOut, available, wrap);
}
int LagrangeInterpolator::process (double actualRatio, const float* in, float* out, int numOut) noexcept
{
return interpolate<LagrangeAlgorithm> (lastInputSamples, subSamplePos, actualRatio, in, out, numOut);
}
int LagrangeInterpolator::processAdding (double actualRatio, const float* in, float* out, int numOut, int available, int wrap, float gain) noexcept
{
return interpolateAdding<LagrangeAlgorithm> (lastInputSamples, subSamplePos, actualRatio, in, out, numOut, available, wrap, gain);
}
int LagrangeInterpolator::processAdding (double actualRatio, const float* in, float* out, int numOut, float gain) noexcept
{
return interpolateAdding<LagrangeAlgorithm> (lastInputSamples, subSamplePos, actualRatio, in, out, numOut, gain);
}
} // namespace juce

View File

@ -0,0 +1,143 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
/**
Interpolator for resampling a stream of floats using 4-point lagrange interpolation.
Note that the resampler is stateful, so when there's a break in the continuity
of the input stream you're feeding it, you should call reset() before feeding
it any new data. And like with any other stateful filter, if you're resampling
multiple channels, make sure each one uses its own LagrangeInterpolator
object.
@see CatmullRomInterpolator
@tags{Audio}
*/
class JUCE_API LagrangeInterpolator
{
public:
LagrangeInterpolator() noexcept;
~LagrangeInterpolator() noexcept;
/** Resets the state of the interpolator.
Call this when there's a break in the continuity of the input data stream.
*/
void reset() noexcept;
/** Resamples a stream of samples.
@param speedRatio the number of input samples to use for each output sample
@param inputSamples the source data to read from. This must contain at
least (speedRatio * numOutputSamplesToProduce) samples.
@param outputSamples the buffer to write the results into
@param numOutputSamplesToProduce the number of output samples that should be created
@returns the actual number of input samples that were used
*/
int process (double speedRatio,
const float* inputSamples,
float* outputSamples,
int numOutputSamplesToProduce) noexcept;
/** Resamples a stream of samples.
@param speedRatio the number of input samples to use for each output sample
@param inputSamples the source data to read from. This must contain at
least (speedRatio * numOutputSamplesToProduce) samples.
@param outputSamples the buffer to write the results into
@param numOutputSamplesToProduce the number of output samples that should be created
@param available the number of available input samples. If it needs more samples
than available, it either wraps back for wrapAround samples, or
it feeds zeroes
@param wrapAround if the stream exceeds available samples, it wraps back for
wrapAround samples. If wrapAround is set to 0, it will feed zeroes.
@returns the actual number of input samples that were used
*/
int process (double speedRatio,
const float* inputSamples,
float* outputSamples,
int numOutputSamplesToProduce,
int available,
int wrapAround) noexcept;
/** Resamples a stream of samples, adding the results to the output data
with a gain.
@param speedRatio the number of input samples to use for each output sample
@param inputSamples the source data to read from. This must contain at
least (speedRatio * numOutputSamplesToProduce) samples.
@param outputSamples the buffer to write the results to - the result values will be added
to any pre-existing data in this buffer after being multiplied by
the gain factor
@param numOutputSamplesToProduce the number of output samples that should be created
@param gain a gain factor to multiply the resulting samples by before
adding them to the destination buffer
@returns the actual number of input samples that were used
*/
int processAdding (double speedRatio,
const float* inputSamples,
float* outputSamples,
int numOutputSamplesToProduce,
float gain) noexcept;
/** Resamples a stream of samples, adding the results to the output data
with a gain.
@param speedRatio the number of input samples to use for each output sample
@param inputSamples the source data to read from. This must contain at
least (speedRatio * numOutputSamplesToProduce) samples.
@param outputSamples the buffer to write the results to - the result values will be added
to any pre-existing data in this buffer after being multiplied by
the gain factor
@param numOutputSamplesToProduce the number of output samples that should be created
@param available the number of available input samples. If it needs more samples
than available, it either wraps back for wrapAround samples, or
it feeds zeroes
@param wrapAround if the stream exceeds available samples, it wraps back for
wrapAround samples. If wrapAround is set to 0, it will feed zeroes.
@param gain a gain factor to multiply the resulting samples by before
adding them to the destination buffer
@returns the actual number of input samples that were used
*/
int processAdding (double speedRatio,
const float* inputSamples,
float* outputSamples,
int numOutputSamplesToProduce,
int available,
int wrapAround,
float gain) noexcept;
private:
float lastInputSamples[5];
double subSamplePos;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LagrangeInterpolator)
};
} // namespace juce

View File

@ -0,0 +1,215 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
Utility class for linearly smoothed values like volume etc. that should
not change abruptly but as a linear ramp, to avoid audio glitches.
@tags{Audio}
*/
template <typename FloatType>
class LinearSmoothedValue
{
public:
/** Constructor. */
LinearSmoothedValue() noexcept
{
}
/** Constructor. */
LinearSmoothedValue (FloatType initialValue) noexcept
: currentValue (initialValue), target (initialValue)
{
}
//==============================================================================
/** Reset to a new sample rate and ramp length.
@param sampleRate The sampling rate
@param rampLengthInSeconds The duration of the ramp in seconds
*/
void reset (double sampleRate, double rampLengthInSeconds) noexcept
{
jassert (sampleRate > 0 && rampLengthInSeconds >= 0);
stepsToTarget = (int) std::floor (rampLengthInSeconds * sampleRate);
currentValue = target;
countdown = 0;
}
//==============================================================================
/** Set a new target value.
@param newValue The new target value
@param force If true, the value will be set immediately, bypassing the ramp
*/
void setValue (FloatType newValue, bool force = false) noexcept
{
if (force)
{
target = currentValue = newValue;
countdown = 0;
return;
}
if (target != newValue)
{
target = newValue;
countdown = stepsToTarget;
if (countdown <= 0)
currentValue = target;
else
step = (target - currentValue) / (FloatType) countdown;
}
}
//==============================================================================
/** Compute the next value.
@returns Smoothed value
*/
FloatType getNextValue() noexcept
{
if (countdown <= 0)
return target;
--countdown;
currentValue += step;
return currentValue;
}
/** Returns true if the current value is currently being interpolated. */
bool isSmoothing() const noexcept
{
return countdown > 0;
}
/** Returns the target value towards which the smoothed value is currently moving. */
FloatType getTargetValue() const noexcept
{
return target;
}
//==============================================================================
/** Applies a linear smoothed gain to a stream of samples
S[i] *= gain
@param samples Pointer to a raw array of samples
@param numSamples Length of array of samples
*/
void applyGain (FloatType* samples, int numSamples) noexcept
{
jassert(numSamples >= 0);
if (isSmoothing())
{
for (int i = 0; i < numSamples; i++)
samples[i] *= getNextValue();
}
else
{
FloatVectorOperations::multiply (samples, target, numSamples);
}
}
//==============================================================================
/** Computes output as linear smoothed gain applied to a stream of samples.
Sout[i] = Sin[i] * gain
@param samplesOut A pointer to a raw array of output samples
@param samplesIn A pointer to a raw array of input samples
@param numSamples The length of the array of samples
*/
void applyGain (FloatType* samplesOut, const FloatType* samplesIn, int numSamples) noexcept
{
jassert (numSamples >= 0);
if (isSmoothing())
{
for (int i = 0; i < numSamples; i++)
samplesOut[i] = samplesIn[i] * getNextValue();
}
else
{
FloatVectorOperations::multiply (samplesOut, samplesIn, target, numSamples);
}
}
//==============================================================================
/** Applies a linear smoothed gain to a buffer */
void applyGain (AudioBuffer<FloatType>& buffer, int numSamples) noexcept
{
jassert (numSamples >= 0);
if (isSmoothing())
{
if (buffer.getNumChannels() == 1)
{
FloatType* samples = buffer.getWritePointer(0);
for (int i = 0; i < numSamples; i++)
samples[i] *= getNextValue();
}
else
{
for (int i = 0; i < numSamples; i++)
{
const FloatType gain = getNextValue();
for (int channel = 0; channel < buffer.getNumChannels(); channel++)
buffer.setSample (channel, i, buffer.getSample (channel, i) * gain);
}
}
}
else
{
buffer.applyGain (0, numSamples, target);
}
}
//==============================================================================
/** Skip the next numSamples samples.
This is identical to calling getNextValue numSamples times.
@see getNextValue
*/
void skip (int numSamples) noexcept
{
if (numSamples >= countdown)
{
currentValue = target;
countdown = 0;
}
else
{
currentValue += (step * static_cast<FloatType> (numSamples));
countdown -= numSamples;
}
}
private:
//==============================================================================
FloatType currentValue = 0, target = 0, step = 0;
int countdown = 0, stepsToTarget = 0;
};
} // namespace juce

View File

@ -0,0 +1,322 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
Performs a simple reverb effect on a stream of audio data.
This is a simple stereo reverb, based on the technique and tunings used in FreeVerb.
Use setSampleRate() to prepare it, and then call processStereo() or processMono() to
apply the reverb to your audio data.
@see ReverbAudioSource
@tags{Audio}
*/
class Reverb
{
public:
//==============================================================================
Reverb()
{
setParameters (Parameters());
setSampleRate (44100.0);
}
//==============================================================================
/** Holds the parameters being used by a Reverb object. */
struct Parameters
{
Parameters() noexcept
: roomSize (0.5f),
damping (0.5f),
wetLevel (0.33f),
dryLevel (0.4f),
width (1.0f),
freezeMode (0)
{}
float roomSize; /**< Room size, 0 to 1.0, where 1.0 is big, 0 is small. */
float damping; /**< Damping, 0 to 1.0, where 0 is not damped, 1.0 is fully damped. */
float wetLevel; /**< Wet level, 0 to 1.0 */
float dryLevel; /**< Dry level, 0 to 1.0 */
float width; /**< Reverb width, 0 to 1.0, where 1.0 is very wide. */
float freezeMode; /**< Freeze mode - values < 0.5 are "normal" mode, values > 0.5
put the reverb into a continuous feedback loop. */
};
//==============================================================================
/** Returns the reverb's current parameters. */
const Parameters& getParameters() const noexcept { return parameters; }
/** Applies a new set of parameters to the reverb.
Note that this doesn't attempt to lock the reverb, so if you call this in parallel with
the process method, you may get artifacts.
*/
void setParameters (const Parameters& newParams)
{
const float wetScaleFactor = 3.0f;
const float dryScaleFactor = 2.0f;
const float wet = newParams.wetLevel * wetScaleFactor;
dryGain.setValue (newParams.dryLevel * dryScaleFactor);
wetGain1.setValue (0.5f * wet * (1.0f + newParams.width));
wetGain2.setValue (0.5f * wet * (1.0f - newParams.width));
gain = isFrozen (newParams.freezeMode) ? 0.0f : 0.015f;
parameters = newParams;
updateDamping();
}
//==============================================================================
/** Sets the sample rate that will be used for the reverb.
You must call this before the process methods, in order to tell it the correct sample rate.
*/
void setSampleRate (const double sampleRate)
{
jassert (sampleRate > 0);
static const short combTunings[] = { 1116, 1188, 1277, 1356, 1422, 1491, 1557, 1617 }; // (at 44100Hz)
static const short allPassTunings[] = { 556, 441, 341, 225 };
const int stereoSpread = 23;
const int intSampleRate = (int) sampleRate;
for (int i = 0; i < numCombs; ++i)
{
comb[0][i].setSize ((intSampleRate * combTunings[i]) / 44100);
comb[1][i].setSize ((intSampleRate * (combTunings[i] + stereoSpread)) / 44100);
}
for (int i = 0; i < numAllPasses; ++i)
{
allPass[0][i].setSize ((intSampleRate * allPassTunings[i]) / 44100);
allPass[1][i].setSize ((intSampleRate * (allPassTunings[i] + stereoSpread)) / 44100);
}
const double smoothTime = 0.01;
damping .reset (sampleRate, smoothTime);
feedback.reset (sampleRate, smoothTime);
dryGain .reset (sampleRate, smoothTime);
wetGain1.reset (sampleRate, smoothTime);
wetGain2.reset (sampleRate, smoothTime);
}
/** Clears the reverb's buffers. */
void reset()
{
for (int j = 0; j < numChannels; ++j)
{
for (int i = 0; i < numCombs; ++i)
comb[j][i].clear();
for (int i = 0; i < numAllPasses; ++i)
allPass[j][i].clear();
}
}
//==============================================================================
/** Applies the reverb to two stereo channels of audio data. */
void processStereo (float* const left, float* const right, const int numSamples) noexcept
{
jassert (left != nullptr && right != nullptr);
for (int i = 0; i < numSamples; ++i)
{
const float input = (left[i] + right[i]) * gain;
float outL = 0, outR = 0;
const float damp = damping.getNextValue();
const float feedbck = feedback.getNextValue();
for (int j = 0; j < numCombs; ++j) // accumulate the comb filters in parallel
{
outL += comb[0][j].process (input, damp, feedbck);
outR += comb[1][j].process (input, damp, feedbck);
}
for (int j = 0; j < numAllPasses; ++j) // run the allpass filters in series
{
outL = allPass[0][j].process (outL);
outR = allPass[1][j].process (outR);
}
const float dry = dryGain.getNextValue();
const float wet1 = wetGain1.getNextValue();
const float wet2 = wetGain2.getNextValue();
left[i] = outL * wet1 + outR * wet2 + left[i] * dry;
right[i] = outR * wet1 + outL * wet2 + right[i] * dry;
}
}
/** Applies the reverb to a single mono channel of audio data. */
void processMono (float* const samples, const int numSamples) noexcept
{
jassert (samples != nullptr);
for (int i = 0; i < numSamples; ++i)
{
const float input = samples[i] * gain;
float output = 0;
const float damp = damping.getNextValue();
const float feedbck = feedback.getNextValue();
for (int j = 0; j < numCombs; ++j) // accumulate the comb filters in parallel
output += comb[0][j].process (input, damp, feedbck);
for (int j = 0; j < numAllPasses; ++j) // run the allpass filters in series
output = allPass[0][j].process (output);
const float dry = dryGain.getNextValue();
const float wet1 = wetGain1.getNextValue();
samples[i] = output * wet1 + samples[i] * dry;
}
}
private:
//==============================================================================
static bool isFrozen (const float freezeMode) noexcept { return freezeMode >= 0.5f; }
void updateDamping() noexcept
{
const float roomScaleFactor = 0.28f;
const float roomOffset = 0.7f;
const float dampScaleFactor = 0.4f;
if (isFrozen (parameters.freezeMode))
setDamping (0.0f, 1.0f);
else
setDamping (parameters.damping * dampScaleFactor,
parameters.roomSize * roomScaleFactor + roomOffset);
}
void setDamping (const float dampingToUse, const float roomSizeToUse) noexcept
{
damping.setValue (dampingToUse);
feedback.setValue (roomSizeToUse);
}
//==============================================================================
class CombFilter
{
public:
CombFilter() noexcept : bufferSize (0), bufferIndex (0), last (0) {}
void setSize (const int size)
{
if (size != bufferSize)
{
bufferIndex = 0;
buffer.malloc (size);
bufferSize = size;
}
clear();
}
void clear() noexcept
{
last = 0;
buffer.clear ((size_t) bufferSize);
}
float process (const float input, const float damp, const float feedbackLevel) noexcept
{
const float output = buffer[bufferIndex];
last = (output * (1.0f - damp)) + (last * damp);
JUCE_UNDENORMALISE (last);
float temp = input + (last * feedbackLevel);
JUCE_UNDENORMALISE (temp);
buffer[bufferIndex] = temp;
bufferIndex = (bufferIndex + 1) % bufferSize;
return output;
}
private:
HeapBlock<float> buffer;
int bufferSize, bufferIndex;
float last;
JUCE_DECLARE_NON_COPYABLE (CombFilter)
};
//==============================================================================
class AllPassFilter
{
public:
AllPassFilter() noexcept : bufferSize (0), bufferIndex (0) {}
void setSize (const int size)
{
if (size != bufferSize)
{
bufferIndex = 0;
buffer.malloc (size);
bufferSize = size;
}
clear();
}
void clear() noexcept
{
buffer.clear ((size_t) bufferSize);
}
float process (const float input) noexcept
{
const float bufferedValue = buffer [bufferIndex];
float temp = input + (bufferedValue * 0.5f);
JUCE_UNDENORMALISE (temp);
buffer [bufferIndex] = temp;
bufferIndex = (bufferIndex + 1) % bufferSize;
return bufferedValue - input;
}
private:
HeapBlock<float> buffer;
int bufferSize, bufferIndex;
JUCE_DECLARE_NON_COPYABLE (AllPassFilter)
};
//==============================================================================
enum { numCombs = 8, numAllPasses = 4, numChannels = 2 };
Parameters parameters;
float gain;
CombFilter comb [numChannels][numCombs];
AllPassFilter allPass [numChannels][numAllPasses];
LinearSmoothedValue<float> damping, feedback, dryGain, wetGain1, wetGain2;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Reverb)
};
} // namespace juce