1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-13 00:04:19 +00:00

Added Animated App template and examples

This commit is contained in:
Felix Faire 2014-10-29 15:55:23 +00:00
parent fefcf7aca6
commit ff6520a89a
1141 changed files with 438491 additions and 94 deletions

View file

@ -0,0 +1,104 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2013 - Raw Material Software Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found at: www.gnu.org/licenses
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.juce.com for more information.
==============================================================================
*/
#ifndef JUCE_DECIBELS_H_INCLUDED
#define JUCE_DECIBELS_H_INCLUDED
//==============================================================================
/**
This class contains some helpful static methods for dealing with decibel values.
*/
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 (const Type decibels,
const 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 (const Type gain,
const Type minusInfinityDb = (Type) defaultMinusInfinitydB)
{
return gain > Type() ? jmax (minusInfinityDb, (Type) std::log10 (gain) * (Type) 20.0)
: minusInfinityDb;
}
//==============================================================================
/** Converts a decibel reading to a string, with the 'dB' suffix.
If the decibel value is lower than minusInfinityDb, the return value will
be "-INF dB".
*/
template <typename Type>
static String toString (const Type decibels,
const int decimalPlaces = 2,
const Type minusInfinityDb = (Type) defaultMinusInfinitydB)
{
String s;
if (decibels <= minusInfinityDb)
{
s = "-INF dB";
}
else
{
if (decibels >= Type())
s << '+';
s << String (decibels, decimalPlaces) << " dB";
}
return s;
}
private:
//==============================================================================
enum
{
defaultMinusInfinitydB = -100
};
Decibels(); // This class can't be instantiated, it's just a holder for static methods..
JUCE_DECLARE_NON_COPYABLE (Decibels)
};
#endif // JUCE_DECIBELS_H_INCLUDED

View file

@ -0,0 +1,244 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2013 - Raw Material Software Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found at: www.gnu.org/licenses
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.juce.com for more information.
==============================================================================
*/
#if JUCE_INTEL
#define JUCE_SNAP_TO_ZERO(n) if (! (n < -1.0e-8 || n > 1.0e-8)) n = 0;
#else
#define JUCE_SNAP_TO_ZERO(n)
#endif
//==============================================================================
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
{
const double 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 (const double sampleRate,
const double frequency) noexcept
{
jassert (sampleRate > 0);
const double n = 1.0 / tan (double_Pi * frequency / sampleRate);
const double nSquared = n * n;
const double c1 = 1.0 / (1.0 + std::sqrt (2.0) * n + nSquared);
return IIRCoefficients (c1,
c1 * 2.0,
c1,
1.0,
c1 * 2.0 * (1.0 - nSquared),
c1 * (1.0 - std::sqrt (2.0) * n + nSquared));
}
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);
return IIRCoefficients (c1,
c1 * -2.0,
c1,
1.0,
c1 * 2.0 * (nSquared - 1.0),
c1 * (1.0 - std::sqrt (2.0) * n + nSquared));
}
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, 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;
const double coso = std::cos (omega);
const double beta = std::sin (omega) * std::sqrt (A) / Q;
const double 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 (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, 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;
const double coso = std::cos (omega);
const double beta = std::sin (omega) * std::sqrt (A) / Q;
const double 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 (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, 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;
return IIRCoefficients (1.0 + alphaTimesA,
c2,
1.0 - alphaTimesA,
1.0 + alphaOverA,
c2,
1.0 - alphaOverA);
}
//==============================================================================
IIRFilter::IIRFilter() noexcept
: v1 (0), v2 (0), active (false)
{
}
IIRFilter::IIRFilter (const IIRFilter& other) noexcept
: v1 (0), v2 (0), 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;
}
float IIRFilter::processSingleSampleRaw (const float in) noexcept
{
float 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)
{
const float c0 = coefficients.coefficients[0];
const float c1 = coefficients.coefficients[1];
const float c2 = coefficients.coefficients[2];
const float c3 = coefficients.coefficients[3];
const float c4 = coefficients.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;
}
JUCE_SNAP_TO_ZERO (lv1); v1 = lv1;
JUCE_SNAP_TO_ZERO (lv2); v2 = lv2;
}
}
#undef JUCE_SNAP_TO_ZERO

View file

@ -0,0 +1,174 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2013 - Raw Material Software Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found at: www.gnu.org/licenses
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.juce.com for more information.
==============================================================================
*/
#ifndef JUCE_IIRFILTER_H_INCLUDED
#define JUCE_IIRFILTER_H_INCLUDED
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;
/** 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 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;
//==============================================================================
/** 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
*/
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, v2;
bool active;
IIRFilter& operator= (const IIRFilter&);
JUCE_LEAK_DETECTOR (IIRFilter)
};
#endif // JUCE_IIRFILTER_H_INCLUDED

View file

@ -0,0 +1,200 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2013 - Raw Material Software Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found at: www.gnu.org/licenses
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.juce.com for more information.
==============================================================================
*/
namespace LagrangeHelpers
{
template <int k>
struct ResampleHelper
{
static forcedinline void calc (float& a, float b) { a *= b * (1.0f / k); }
};
template<>
struct ResampleHelper <0>
{
static forcedinline void calc (float&, float) {}
};
template <int k>
static forcedinline float calcCoefficient (float input, const float offset) noexcept
{
ResampleHelper <0 - k>::calc (input, -2.0f - offset);
ResampleHelper <1 - k>::calc (input, -1.0f - offset);
ResampleHelper <2 - k>::calc (input, 0.0f - offset);
ResampleHelper <3 - k>::calc (input, 1.0f - offset);
ResampleHelper <4 - k>::calc (input, 2.0f - offset);
return input;
}
static forcedinline float valueAtOffset (const float* const inputs, const 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);
}
static forcedinline void push (float* inputs, const float newValue) noexcept
{
inputs[4] = inputs[3];
inputs[3] = inputs[2];
inputs[2] = inputs[1];
inputs[1] = inputs[0];
inputs[0] = newValue;
}
}
//==============================================================================
LagrangeInterpolator::LagrangeInterpolator() { reset(); }
LagrangeInterpolator::~LagrangeInterpolator() {}
void LagrangeInterpolator::reset() noexcept
{
subSamplePos = 1.0;
for (int i = 0; i < numElementsInArray (lastInputSamples); ++i)
lastInputSamples[i] = 0;
}
int LagrangeInterpolator::process (const double actualRatio, const float* in,
float* out, const int numOut) noexcept
{
if (actualRatio == 1.0)
{
memcpy (out, in, (size_t) numOut * sizeof (float));
if (numOut >= 4)
{
memcpy (lastInputSamples, in + (numOut - 4), 4 * sizeof (float));
}
else
{
for (int i = 0; i < numOut; ++i)
LagrangeHelpers::push (lastInputSamples, in[i]);
}
return numOut;
}
const float* const originalIn = in;
double pos = subSamplePos;
if (actualRatio < 1.0)
{
for (int i = numOut; --i >= 0;)
{
if (pos >= 1.0)
{
LagrangeHelpers::push (lastInputSamples, *in++);
pos -= 1.0;
}
*out++ = LagrangeHelpers::valueAtOffset (lastInputSamples, (float) pos);
pos += actualRatio;
}
}
else
{
for (int i = numOut; --i >= 0;)
{
while (pos < actualRatio)
{
LagrangeHelpers::push (lastInputSamples, *in++);
pos += 1.0;
}
pos -= actualRatio;
*out++ = LagrangeHelpers::valueAtOffset (lastInputSamples, 1.0f - (float) pos);
}
}
subSamplePos = pos;
return (int) (in - originalIn);
}
int LagrangeInterpolator::processAdding (const double actualRatio, const float* in,
float* out, const int numOut, const float gain) noexcept
{
if (actualRatio == 1.0)
{
if (gain != 1.0f)
{
for (int i = 0; i < numOut; ++i)
out[i] += in[i] * gain;
}
else
{
for (int i = 0; i < numOut; ++i)
out[i] += in[i];
}
if (numOut >= 4)
{
memcpy (lastInputSamples, in + (numOut - 4), 4 * sizeof (float));
}
else
{
for (int i = 0; i < numOut; ++i)
LagrangeHelpers::push (lastInputSamples, in[i]);
}
return numOut;
}
const float* const originalIn = in;
double pos = subSamplePos;
if (actualRatio < 1.0)
{
for (int i = numOut; --i >= 0;)
{
if (pos >= 1.0)
{
LagrangeHelpers::push (lastInputSamples, *in++);
pos -= 1.0;
}
*out++ += gain * LagrangeHelpers::valueAtOffset (lastInputSamples, (float) pos);
pos += actualRatio;
}
}
else
{
for (int i = numOut; --i >= 0;)
{
while (pos < actualRatio)
{
LagrangeHelpers::push (lastInputSamples, *in++);
pos += 1.0;
}
pos -= actualRatio;
*out++ += gain * LagrangeHelpers::valueAtOffset (lastInputSamples, jmax (0.0f, 1.0f - (float) pos));
}
}
subSamplePos = pos;
return (int) (in - originalIn);
}

View file

@ -0,0 +1,94 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2013 - Raw Material Software Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found at: www.gnu.org/licenses
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.juce.com for more information.
==============================================================================
*/
#ifndef JUCE_LAGRANGEINTERPOLATOR_H_INCLUDED
#define JUCE_LAGRANGEINTERPOLATOR_H_INCLUDED
//==============================================================================
/**
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.
*/
class JUCE_API LagrangeInterpolator
{
public:
LagrangeInterpolator();
~LagrangeInterpolator();
/** 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, 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;
private:
float lastInputSamples[5];
double subSamplePos;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LagrangeInterpolator)
};
#endif // JUCE_LAGRANGEINTERPOLATOR_H_INCLUDED

View file

@ -0,0 +1,323 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2013 - Raw Material Software Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found at: www.gnu.org/licenses
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.juce.com for more information.
==============================================================================
*/
#ifndef JUCE_REVERB_H_INCLUDED
#define JUCE_REVERB_H_INCLUDED
//==============================================================================
/**
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
*/
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;
wet1 = wet * (newParams.width * 0.5f + 0.5f);
wet2 = wet * (1.0f - newParams.width) * 0.5f;
dry = newParams.dryLevel * dryScaleFactor;
gain = isFrozen (newParams.freezeMode) ? 0.0f : 0.015f;
parameters = newParams;
shouldUpdateDamping = true;
}
//==============================================================================
/** 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);
}
shouldUpdateDamping = true;
}
/** 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);
if (shouldUpdateDamping)
updateDamping();
for (int i = 0; i < numSamples; ++i)
{
const float input = (left[i] + right[i]) * gain;
float outL = 0, outR = 0;
for (int j = 0; j < numCombs; ++j) // accumulate the comb filters in parallel
{
outL += comb[0][j].process (input);
outR += comb[1][j].process (input);
}
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);
}
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);
if (shouldUpdateDamping)
updateDamping();
for (int i = 0; i < numSamples; ++i)
{
const float input = samples[i] * gain;
float output = 0;
for (int j = 0; j < numCombs; ++j) // accumulate the comb filters in parallel
output += comb[0][j].process (input);
for (int j = 0; j < numAllPasses; ++j) // run the allpass filters in series
output = allPass[0][j].process (output);
samples[i] = output * wet1 + samples[i] * dry;
}
}
private:
//==============================================================================
Parameters parameters;
volatile bool shouldUpdateDamping;
float gain, wet1, wet2, dry;
inline 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;
shouldUpdateDamping = false;
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
{
for (int j = 0; j < numChannels; ++j)
for (int i = numCombs; --i >= 0;)
comb[j][i].setFeedbackAndDamp (roomSizeToUse, dampingToUse);
}
//==============================================================================
class CombFilter
{
public:
CombFilter() noexcept
: bufferSize (0), bufferIndex (0),
feedback (0), last (0), damp1 (0), damp2 (0)
{}
void setSize (const int size)
{
if (size != bufferSize)
{
bufferIndex = 0;
buffer.malloc ((size_t) size);
bufferSize = size;
}
clear();
}
void clear() noexcept
{
last = 0;
buffer.clear ((size_t) bufferSize);
}
void setFeedbackAndDamp (const float f, const float d) noexcept
{
damp1 = d;
damp2 = 1.0f - d;
feedback = f;
}
inline float process (const float input) noexcept
{
const float output = buffer [bufferIndex];
last = (output * damp2) + (last * damp1);
JUCE_UNDENORMALISE (last);
float temp = input + (last * feedback);
JUCE_UNDENORMALISE (temp);
buffer [bufferIndex] = temp;
bufferIndex = (bufferIndex + 1) % bufferSize;
return output;
}
private:
HeapBlock<float> buffer;
int bufferSize, bufferIndex;
float feedback, last, damp1, damp2;
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_t) size);
bufferSize = size;
}
clear();
}
void clear() noexcept
{
buffer.clear ((size_t) bufferSize);
}
inline 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 };
CombFilter comb [numChannels][numCombs];
AllPassFilter allPass [numChannels][numAllPasses];
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Reverb)
};
#endif // JUCE_REVERB_H_INCLUDED