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:
parent
fefcf7aca6
commit
ff6520a89a
1141 changed files with 438491 additions and 94 deletions
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
Loading…
Add table
Add a link
Reference in a new issue