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

Added a LogSmoothedValue class

This commit is contained in:
Tom Poole 2019-02-15 14:00:36 +00:00
parent 6f2f9afb06
commit 94f1641402
5 changed files with 725 additions and 242 deletions

View file

@ -60,6 +60,7 @@
#include "utilities/juce_IIRFilter.cpp"
#include "utilities/juce_LagrangeInterpolator.cpp"
#include "utilities/juce_CatmullRomInterpolator.cpp"
#include "utilities/juce_SmoothedValues.cpp"
#include "midi/juce_MidiBuffer.cpp"
#include "midi/juce_MidiFile.cpp"
#include "midi/juce_MidiKeyboardState.cpp"

View file

@ -89,7 +89,7 @@
#include "utilities/juce_IIRFilter.h"
#include "utilities/juce_LagrangeInterpolator.h"
#include "utilities/juce_CatmullRomInterpolator.h"
#include "utilities/juce_LinearSmoothedValue.h"
#include "utilities/juce_SmoothedValues.h"
#include "utilities/juce_Reverb.h"
#include "utilities/juce_ADSR.h"
#include "midi/juce_MidiMessage.h"

View file

@ -1,241 +0,0 @@
/*
==============================================================================
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() = default;
/** Constructor. */
LinearSmoothedValue (FloatType initialValue) noexcept
: currentValue (initialValue), target (initialValue)
{
}
//==============================================================================
/** Set a new sample rate and ramp length in seconds.
@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);
reset ((int) std::floor (rampLengthInSeconds * sampleRate));
}
/** Set a new ramp length directly in samples.
@param numSteps The number of samples over which the ramp should be active
*/
void reset (int numSteps) noexcept
{
stepsToTarget = numSteps;
setCurrentAndTargetValue (target);
}
/** Set the next value to ramp towards.
@param newValue The new target value
*/
void setTargetValue (FloatType newValue) noexcept
{
if (target == newValue)
return;
target = newValue;
if (stepsToTarget <= 0)
{
setCurrentAndTargetValue (target);
return;
}
countdown = stepsToTarget;
step = (target - currentValue) / static_cast<FloatType> (countdown);
}
/** Sets the current value and the target value.
@param newValue the new value to take
*/
void setCurrentAndTargetValue (FloatType newValue)
{
target = currentValue = newValue;
countdown = 0;
}
//==============================================================================
/** Compute the next value.
@returns Smoothed value
*/
FloatType getNextValue() noexcept
{
if (! isSmoothing())
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 current value of the ramp. */
FloatType getCurrentValue() const noexcept { return currentValue; }
/** 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)
{
auto samples = buffer.getWritePointer(0);
for (int i = 0; i < numSamples; ++i)
samples[i] *= getNextValue();
}
else
{
for (int i = 0; i < numSamples; ++i)
{
auto 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. It returns
the new current value.
@see getNextValue
*/
FloatType skip (int numSamples) noexcept
{
if (numSamples >= countdown)
{
setCurrentAndTargetValue (target);
return target;
}
currentValue += (step * static_cast<FloatType> (numSamples));
countdown -= numSamples;
return currentValue;
}
//==============================================================================
/** THIS FUNCTION IS DEPRECATED.
Use `setTargetValue (float)` and `setCurrentAndTargetValue()` instead:
lsv.setValue (x, false); -> lsv.setTargetValue (x);
lsv.setValue (x, true); -> lsv.setCurrentAndTargetValue (x);
@param newValue The new target value
@param force If true, the value will be set immediately, bypassing the ramp
*/
JUCE_DEPRECATED_WITH_BODY (void setValue (FloatType newValue, bool force = false) noexcept,
{
if (force)
{
target = newValue;
setCurrentAndTargetValue (target);
return;
}
setTargetValue (newValue);
})
private:
//==============================================================================
FloatType currentValue = 0, target = 0, step = 0;
int countdown = 0, stepsToTarget = 0;
};
} // namespace juce

View file

@ -0,0 +1,285 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2018 - 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
{
#if JUCE_UNIT_TESTS
template <class SmoothedValueType>
class CommonSmoothedValueTests : public UnitTest
{
public:
CommonSmoothedValueTests()
: UnitTest ("CommonSmoothedValueTests", "SmoothedValues")
{}
void runTest() override
{
beginTest ("Initial state");
{
SmoothedValueType lsv;
auto value = lsv.getCurrentValue();
expectEquals (lsv.getTargetValue(), value);
lsv.getNextValue();
expectEquals (lsv.getCurrentValue(), value);
expect (! lsv.isSmoothing());
}
beginTest ("Resetting");
{
auto initialValue = -5.0f;
SmoothedValueType lsv (-5.0f);
lsv.reset (3);
expectEquals (lsv.getCurrentValue(), initialValue);
auto targetValue = initialValue + 1.0f;
lsv.setTargetValue (targetValue);
expectEquals (lsv.getTargetValue(), targetValue);
expectEquals (lsv.getCurrentValue(), initialValue);
expect (lsv.isSmoothing());
auto currentValue = lsv.getNextValue();
expect (currentValue > initialValue);
expectEquals (lsv.getCurrentValue(), currentValue);
expectEquals (lsv.getTargetValue(), targetValue);
expect (lsv.isSmoothing());
lsv.reset (5);
expectEquals (lsv.getCurrentValue(), targetValue);
expectEquals (lsv.getTargetValue(), targetValue);
expect (! lsv.isSmoothing());
lsv.getNextValue();
expectEquals (lsv.getCurrentValue(), targetValue);
lsv.setTargetValue (-15.0f);
lsv.getNextValue();
float newStart = -20.0f;
lsv.setCurrentAndTargetValue (newStart);
expectEquals (lsv.getNextValue(), newStart);
expectEquals (lsv.getTargetValue(), newStart);
expectEquals (lsv.getCurrentValue(), newStart);
expect (! lsv.isSmoothing());
}
beginTest ("Sample rate");
{
SmoothedValueType lsvSamples { 3.0f };
auto lsvTime = lsvSamples;
auto numSamples = 12;
lsvSamples.reset (numSamples);
lsvTime.reset (numSamples * 2, 1.0);
for (int i = 0; i < numSamples; ++i)
{
lsvTime.skip (1);
expectWithinAbsoluteError (lsvSamples.getNextValue(),
lsvTime.getNextValue(),
1.0e-7f);
}
}
beginTest ("Block processing");
{
SmoothedValueType lsv (1.0f);
lsv.reset (12);
lsv.setTargetValue (2.0f);
const auto numSamples = 15;
AudioBuffer<float> referenceData (1, numSamples);
for (int i = 0; i < numSamples; ++i)
referenceData.setSample (0, i, lsv.getNextValue());
expect (referenceData.getSample (0, 0) > 0);
expect (referenceData.getSample (0, 10) < lsv.getTargetValue());
expectWithinAbsoluteError (referenceData.getSample (0, 11),
lsv.getTargetValue(),
1.0e-7f);
auto getUnitData = [] (int numSamplesToGenerate)
{
AudioBuffer<float> result (1, numSamplesToGenerate);
for (int i = 0; i < numSamplesToGenerate; ++i)
result.setSample (0, i, 1.0f);
return result;
};
auto compareData = [this](const AudioBuffer<float>& test,
const AudioBuffer<float>& reference)
{
for (int i = 0; i < test.getNumSamples(); ++i)
expectWithinAbsoluteError (test.getSample (0, i),
reference.getSample (0, i),
1.0e-7f);
};
auto testData = getUnitData (numSamples);
lsv.setCurrentAndTargetValue (1.0f);
lsv.setTargetValue (2.0f);
lsv.applyGain (testData.getWritePointer (0), numSamples);
compareData (testData, referenceData);
testData = getUnitData (numSamples);
AudioBuffer<float> destData (1, numSamples);
lsv.setCurrentAndTargetValue (1.0f);
lsv.setTargetValue (2.0f);
lsv.applyGain (destData.getWritePointer (0),
testData.getReadPointer (0),
numSamples);
compareData (destData, referenceData);
compareData (testData, getUnitData (numSamples));
testData = getUnitData (numSamples);
lsv.setCurrentAndTargetValue (1.0f);
lsv.setTargetValue (2.0f);
lsv.applyGain (testData, numSamples);
compareData (testData, referenceData);
}
beginTest ("Skip");
{
SmoothedValueType lsv;
lsv.reset (12);
lsv.setCurrentAndTargetValue (0.0f);
lsv.setTargetValue (1.0f);
Array<float> reference;
for (int i = 0; i < 15; ++i)
reference.add (lsv.getNextValue());
lsv.setCurrentAndTargetValue (0.0f);
lsv.setTargetValue (1.0f);
expectWithinAbsoluteError (lsv.skip (1), reference[0], 1.0e-7f);
expectWithinAbsoluteError (lsv.skip (1), reference[1], 1.0e-7f);
expectWithinAbsoluteError (lsv.skip (2), reference[3], 1.0e-7f);
lsv.skip (3);
expectWithinAbsoluteError (lsv.getCurrentValue(), reference[6], 1.0e-7f);
expectEquals (lsv.skip (300), lsv.getTargetValue());
expectEquals (lsv.getCurrentValue(), lsv.getTargetValue());
}
beginTest ("Moving target");
{
SmoothedValueType lsv;
lsv.reset (12);
float initialValue = 0.0f;
lsv.setCurrentAndTargetValue (initialValue);
lsv.setTargetValue (1.0f);
auto delta = lsv.getNextValue() - initialValue;
lsv.skip (6);
auto newInitialValue = lsv.getCurrentValue();
lsv.setTargetValue (newInitialValue + 2.0f);
auto doubleDelta = lsv.getNextValue() - newInitialValue;
expectWithinAbsoluteError (doubleDelta, delta * 2.0f, 1.0e-7f);
}
}
};
static CommonSmoothedValueTests<LinearSmoothedValue<float>> commonLinearSmoothedValueTests;
static CommonSmoothedValueTests<LogSmoothedValue <float>> commonLogSmoothedValueTests;
class LogSmoothedValueTests : public UnitTest
{
public:
LogSmoothedValueTests()
: UnitTest ("LogSmoothedValueTests", "SmoothedValues")
{}
void runTest() override
{
beginTest ("Curve");
{
Array<double> levels = { -0.12243, -1.21245, -12.2342, -22.4683, -30.0, -61.18753 };
for (auto level : levels)
{
Array<Range<double>> ranges = { Range<double> (0.0, 1.0),
Range<double> (-2.345, 0.0),
Range<double> (-2.63, 3.56),
Range<double> (3.3, -0.2) };
for (auto range : ranges)
{
LogSmoothedValue<double> slowStart { range.getStart() } , fastStart { range.getEnd() };
auto numSamples = 12;
slowStart.reset (numSamples);
fastStart.reset (numSamples);
slowStart.setLogParameters (level, true);
fastStart.setLogParameters (level, false);
slowStart.setTargetValue (range.getEnd());
fastStart.setTargetValue (range.getStart());
AudioBuffer<double> results (2, numSamples + 1);
results.setSample (0, 0, slowStart.getCurrentValue());
results.setSample (1, 0, fastStart.getCurrentValue());
for (int i = 1; i < results.getNumSamples(); ++i)
{
results.setSample (0, i, slowStart.getNextValue());
results.setSample (1, i, fastStart.getNextValue());
}
for (int i = 0; i < results.getNumSamples(); ++i)
expectWithinAbsoluteError (results.getSample (0, i),
results.getSample (1, results.getNumSamples() - (i + 1)),
1.0e-7);
auto expectedMidpoint = range.getStart() + (range.getLength() * Decibels::decibelsToGain (level));
expectWithinAbsoluteError (results.getSample (0, numSamples / 2),
expectedMidpoint,
1.0e-7);
}
}
}
}
};
static LogSmoothedValueTests logSmoothedValueTests;
#endif
} // namespace juce

View file

@ -0,0 +1,438 @@
/*
==============================================================================
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
{
//==============================================================================
/**
A base class for the smoothed value classes.
@tags{Audio}
*/
template <template<typename> class SmoothedValueClass, typename FloatType>
class SmoothedValueBase
{
public:
//==============================================================================
/** Constructor. */
SmoothedValueBase() = default;
//==============================================================================
/** Returns true if the current value is currently being interpolated. */
bool isSmoothing() const noexcept { return countdown > 0; }
/** Returns the current value of the ramp. */
FloatType getCurrentValue() const noexcept { return currentValue; }
//==============================================================================
/** Returns the target value towards which the smoothed value is currently moving. */
FloatType getTargetValue() const noexcept { return target; }
/** Sets the current value and the target value.
@param newValue the new value to take
*/
void setCurrentAndTargetValue (FloatType newValue)
{
target = currentValue = newValue;
countdown = 0;
}
//==============================================================================
/** Applies a 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] *= getNextSmoothedValue();
}
else
{
FloatVectorOperations::multiply (samples, target, numSamples);
}
}
/** Computes output as a 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] * getNextSmoothedValue();
}
else
{
FloatVectorOperations::multiply (samplesOut, samplesIn, target, numSamples);
}
}
/** Applies a smoothed gain to a buffer */
void applyGain (AudioBuffer<FloatType>& buffer, int numSamples) noexcept
{
jassert (numSamples >= 0);
if (isSmoothing())
{
if (buffer.getNumChannels() == 1)
{
auto* samples = buffer.getWritePointer (0);
for (int i = 0; i < numSamples; ++i)
samples[i] *= getNextSmoothedValue();
}
else
{
for (auto i = 0; i < numSamples; ++i)
{
auto gain = getNextSmoothedValue();
for (int channel = 0; channel < buffer.getNumChannels(); channel++)
buffer.setSample (channel, i, buffer.getSample (channel, i) * gain);
}
}
}
else
{
buffer.applyGain (0, numSamples, target);
}
}
protected:
//==============================================================================
void reset (double sampleRate, double rampLengthInSeconds) noexcept
{
jassert (sampleRate > 0 && rampLengthInSeconds >= 0);
auto& derived = *(static_cast<SmoothedValueClass<FloatType>*> (this));
derived.reset ((int) std::floor (rampLengthInSeconds * sampleRate));
}
//==============================================================================
FloatType currentValue = 0, target = 0;
int countdown = 0;
private:
//==============================================================================
FloatType getNextSmoothedValue() noexcept
{
return static_cast<SmoothedValueClass<FloatType>*> (this)->getNextValue();
}
};
//==============================================================================
/**
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 SmoothedValueBase<LinearSmoothedValue, FloatType>
{
public:
//==============================================================================
/** Constructor. */
LinearSmoothedValue() = default;
/** Constructor. */
LinearSmoothedValue (FloatType initialValue) noexcept
{
// Visual Studio can't handle base class initialisation with CRTP
this->currentValue = initialValue;
this->target = initialValue;
}
//==============================================================================
/** Reset to a new sample rate and ramp length.
@param sampleRate The sample rate
@param rampLengthInSeconds The duration of the ramp in seconds
*/
void reset (double sampleRate, double rampLengthInSeconds) noexcept
{
jassert (sampleRate > 0 && rampLengthInSeconds >= 0);
reset ((int) std::floor (rampLengthInSeconds * sampleRate));
}
/** Set a new ramp length directly in samples.
@param numSteps The number of samples over which the ramp should be active
*/
void reset (int numSteps) noexcept
{
stepsToTarget = numSteps;
this->setCurrentAndTargetValue (this->target);
}
//==============================================================================
/** Set the next value to ramp towards.
@param newValue The new target value
*/
void setTargetValue (FloatType newValue) noexcept
{
if (newValue == this->target)
return;
if (stepsToTarget <= 0)
{
this->setCurrentAndTargetValue (newValue);
return;
}
this->target = newValue;
this->countdown = stepsToTarget;
step = (this->target - this->currentValue) / (FloatType) this->countdown;
}
//==============================================================================
/** Compute the next value.
@returns Smoothed value
*/
FloatType getNextValue() noexcept
{
if (! this->isSmoothing())
return this->target;
--(this->countdown);
this->currentValue = this->isSmoothing() ? this->currentValue + step
: this->target;
return this->currentValue;
}
//==============================================================================
/** Skip the next numSamples samples.
This is identical to calling getNextValue numSamples times. It returns
the new current value.
@see getNextValue
*/
FloatType skip (int numSamples) noexcept
{
if (numSamples >= this->countdown)
{
this->setCurrentAndTargetValue (this->target);
return this->target;
}
this->currentValue += step * (FloatType) numSamples;
this->countdown -= numSamples;
return this->currentValue;
}
//==============================================================================
/** THIS FUNCTION IS DEPRECATED.
Use `setTargetValue (float)` and `setCurrentAndTargetValue()` instead:
lsv.setValue (x, false); -> lsv.setTargetValue (x);
lsv.setValue (x, true); -> lsv.setCurrentAndTargetValue (x);
@param newValue The new target value
@param force If true, the value will be set immediately, bypassing the ramp
*/
JUCE_DEPRECATED_WITH_BODY (void setValue (FloatType newValue, bool force = false) noexcept,
{
if (force)
{
this->setCurrentAndTargetValue (newValue);
return;
}
setTargetValue (newValue);
})
private:
//==============================================================================
FloatType step = FloatType();
int stepsToTarget = 0;
};
//==============================================================================
/**
Utility class for logarithmically smoothed values.
Logarithmically smoothed values can be more relevant than linear ones for
specific cases such as algorithm change smoothing, using two of them in
opposite directions.
@see LinearSmoothedValue
@tags{Audio}
*/
template <typename FloatType>
class LogSmoothedValue : public SmoothedValueBase<LogSmoothedValue, FloatType>
{
public:
//==============================================================================
/** Constructor. */
LogSmoothedValue() = default;
/** Constructor. */
LogSmoothedValue (FloatType initialValue) noexcept
{
// Visual Studio can't handle base class initialisation with CRTP
this->currentValue = initialValue;
this->target = initialValue;
}
//==============================================================================
/** Sets the behaviour of the log ramp.
@param midPointAmplitudedB Sets the amplitude of the mid point in
decibels, with the target value at 0 dB
and the initial value at -inf dB
@param rateOfChangeShouldIncrease If true then the ramp starts shallow
and gets progressively steeper, if false
then the ramp is initially steep and
flattens out as you approach the target
value
*/
void setLogParameters (FloatType midPointAmplitudedB, bool rateOfChangeShouldIncrease) noexcept
{
jassert (midPointAmplitudedB < (FloatType) 0.0);
B = Decibels::decibelsToGain (midPointAmplitudedB);
increasingRateOfChange = rateOfChangeShouldIncrease;
}
//==============================================================================
/** Reset to a new sample rate and ramp length.
@param sampleRate The sample rate
@param rampLengthInSeconds The duration of the ramp in seconds
*/
void reset (double sampleRate, double rampLengthInSeconds) noexcept
{
jassert (sampleRate > 0 && rampLengthInSeconds >= 0);
reset ((int) std::floor (rampLengthInSeconds * sampleRate));
}
/** Set a new ramp length directly in samples.
@param numSteps The number of samples over which the ramp should be active
@param increasingRateOfChange If the log behaviour makes the ramp increase
slowly at the beginning, rather than at the end
*/
void reset (int numSteps) noexcept
{
stepsToTarget = numSteps;
this->setCurrentAndTargetValue (this->target);
updateRampParameters();
}
//==============================================================================
/** 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 setTargetValue (FloatType newValue) noexcept
{
if (newValue == this->target)
return;
if (stepsToTarget <= 0)
{
this->setCurrentAndTargetValue (newValue);
return;
}
this->target = newValue;
this->countdown = stepsToTarget;
source = this->currentValue;
updateRampParameters();
}
//==============================================================================
/** Compute the next value.
@returns Smoothed value
*/
FloatType getNextValue() noexcept
{
if (! this->isSmoothing())
return this->target;
--(this->countdown);
temp *= r; temp += d;
this->currentValue = jmap (temp, source, this->target);
return this->currentValue;
}
//==============================================================================
/** Skip the next numSamples samples.
This is identical to calling getNextValue numSamples times.
@see getNextValue
*/
FloatType skip (int numSamples) noexcept
{
if (numSamples >= this->countdown)
{
this->setCurrentAndTargetValue (this->target);
return this->target;
}
this->countdown -= numSamples;
auto rN = std::pow (r, numSamples);
temp *= rN;
temp += d * (rN - (FloatType) 1) / (r - (FloatType) 1);
this->currentValue = jmap (temp, source, this->target);
return this->currentValue;
}
private:
//==============================================================================
void updateRampParameters()
{
auto D = increasingRateOfChange ? B : (FloatType) 1 - B;
auto base = ((FloatType) 1 / D) - (FloatType) 1;
r = std::pow (base, (FloatType) 2 / (FloatType) stepsToTarget);
auto rN = std::pow (r, (FloatType) stepsToTarget);
d = (r - (FloatType) 1) / (rN - (FloatType) 1);
temp = 0;
}
//==============================================================================
bool increasingRateOfChange = true;
FloatType B = Decibels::decibelsToGain ((FloatType) -40);
int stepsToTarget = 0;
FloatType temp = 0, source = 0, r = 0, d = 1;
};
} // namespace juce