35template <
typename SmoothedValueType>
40 template <
typename T>
struct FloatTypeHelper;
45 using Type = FloatType;
51 using Type = FloatType;
77 target = currentValue = newValue;
93 for (
int i = 0; i < numSamples; ++i)
94 samples[i] *= getNextSmoothedValue();
98 FloatVectorOperations::multiply (
samples, target, numSamples);
114 for (
int i = 0; i < numSamples; ++i)
130 if (buffer.getNumChannels() == 1)
132 auto*
samples = buffer.getWritePointer (0);
134 for (
int i = 0; i < numSamples; ++i)
135 samples[i] *= getNextSmoothedValue();
139 for (
auto i = 0; i < numSamples; ++i)
141 auto gain = getNextSmoothedValue();
143 for (
int channel = 0; channel < buffer.getNumChannels(); channel++)
144 buffer.setSample (channel, i, buffer.getSample (channel, i) * gain);
150 buffer.applyGain (0, numSamples, target);
156 FloatType getNextSmoothedValue() noexcept
163 FloatType currentValue = 0;
164 FloatType target = currentValue;
178namespace ValueSmoothingTypes
225template <
typename FloatType,
typename SmoothingType = ValueSmoothingTypes::Linear>
240 jassert (! (std::is_same_v<SmoothingType, ValueSmoothingTypes::Multiplicative>
245 this->target = this->currentValue;
264 stepsToTarget = numSteps;
277 if (stepsToTarget <= 0)
284 jassert (! (std::is_same_v<SmoothingType, ValueSmoothingTypes::Multiplicative>
287 this->target = newValue;
288 this->countdown = stepsToTarget;
307 this->currentValue = this->target;
309 return this->currentValue;
318 FloatType
skip (
int numSamples)
noexcept
320 if (numSamples >= this->countdown)
326 skipCurrentValue (numSamples);
328 this->countdown -= numSamples;
329 return this->currentValue;
342 [[
deprecated (
"Use setTargetValue and setCurrentAndTargetValue instead.")]]
343 void setValue (FloatType newValue,
bool force =
false) noexcept
357 template <
typename T = SmoothingType>
358 void setStepSize() noexcept
360 if constexpr (std::is_same_v<T, ValueSmoothingTypes::Linear>)
362 step = (this->target - this->currentValue) / (FloatType) this->countdown;
364 else if constexpr (std::is_same_v<T, ValueSmoothingTypes::Multiplicative>)
366 step =
std::exp ((
std::log (std::abs (this->target)) -
std::log (std::abs (this->currentValue))) / (FloatType) this->countdown);
371 template <
typename T = SmoothingType>
372 void setNextValue() noexcept
374 if constexpr (std::is_same_v<T, ValueSmoothingTypes::Linear>)
376 this->currentValue += step;
378 else if constexpr (std::is_same_v<T, ValueSmoothingTypes::Multiplicative>)
380 this->currentValue *= step;
385 template <
typename T = SmoothingType>
386 void skipCurrentValue (
int numSamples)
noexcept
388 if constexpr (std::is_same_v<T, ValueSmoothingTypes::Linear>)
390 this->currentValue += step * (FloatType) numSamples;
392 else if constexpr (std::is_same_v<T, ValueSmoothingTypes::Multiplicative>)
394 this->currentValue *= (FloatType)
std::pow (step, numSamples);
399 FloatType step = FloatType();
400 int stepsToTarget = 0;
403template <
typename FloatType>
411template <
class SmoothedValueType>
419 void runTest()
override
421 beginTest (
"Initial state");
425 auto value =
sv.getCurrentValue();
426 expectEquals (
sv.getTargetValue(), value);
429 expectEquals (
sv.getCurrentValue(), value);
430 expect (!
sv.isSmoothing());
433 beginTest (
"Resetting");
445 expect (
sv.isSmoothing());
447 auto currentValue =
sv.getNextValue();
449 expectEquals (
sv.getCurrentValue(), currentValue);
451 expect (
sv.isSmoothing());
457 expect (!
sv.isSmoothing());
462 sv.setTargetValue (1.5f);
469 expectEquals (
sv.getCurrentValue(),
newStart);
470 expect (!
sv.isSmoothing());
473 beginTest (
"Sample rate");
478 auto numSamples = 12;
481 svTime.reset (numSamples * 2, 1.0);
483 for (
int i = 0; i < numSamples; ++i)
486 expectWithinAbsoluteError (
svSamples.getNextValue(),
492 beginTest (
"Block processing");
497 sv.setTargetValue (2.0f);
499 const auto numSamples = 15;
503 for (
int i = 0; i < numSamples; ++i)
517 result.setSample (0, i, 1.0f);
522 auto compareData = [
this] (
const AudioBuffer<float>& test,
523 const AudioBuffer<float>& reference)
525 for (
int i = 0; i < test.getNumSamples(); ++i)
526 expectWithinAbsoluteError (test.getSample (0, i),
527 reference.getSample (0, i),
531 auto testData = getUnitData (numSamples);
532 sv.setCurrentAndTargetValue (1.0f);
533 sv.setTargetValue (2.0f);
534 sv.applyGain (
testData.getWritePointer (0), numSamples);
537 testData = getUnitData (numSamples);
538 AudioBuffer<float> destData (1, numSamples);
539 sv.setCurrentAndTargetValue (1.0f);
540 sv.setTargetValue (2.0f);
541 sv.applyGain (destData.getWritePointer (0),
547 testData = getUnitData (numSamples);
548 sv.setCurrentAndTargetValue (1.0f);
549 sv.setTargetValue (2.0f);
559 sv.setCurrentAndTargetValue (1.0f);
560 sv.setTargetValue (2.0f);
562 Array<float> reference;
564 for (
int i = 0; i < 15; ++i)
565 reference.add (
sv.getNextValue());
567 sv.setCurrentAndTargetValue (1.0f);
568 sv.setTargetValue (2.0f);
570 expectWithinAbsoluteError (
sv.skip (1), reference[0], 1.0e-6f);
571 expectWithinAbsoluteError (
sv.skip (1), reference[1], 1.0e-6f);
572 expectWithinAbsoluteError (
sv.skip (2), reference[3], 1.0e-6f);
574 expectWithinAbsoluteError (
sv.getCurrentValue(), reference[6], 1.0e-6f);
575 expectEquals (
sv.skip (300),
sv.getTargetValue());
576 expectEquals (
sv.getCurrentValue(),
sv.getTargetValue());
579 beginTest (
"Negative");
587 { -100.0f, -3.0f } };
589 for (
auto range : ranges)
591 auto start = range.first,
end = range.second;
593 sv.setCurrentAndTargetValue (start);
594 sv.setTargetValue (
end);
608 expectEquals (
sv.getNextValue(),
end);
609 expectEquals (
sv.getCurrentValue(),
end);
611 sv.setCurrentAndTargetValue (start);
612 sv.setTargetValue (
end);
619 expectEquals (
sv.getNextValue(), -
positiveSv.getNextValue());
A multi-channel buffer containing floating point audio samples.
A base class for the smoothed value classes.
bool isSmoothing() const noexcept
Returns true if the current value is currently being interpolated.
SmoothedValueBase()=default
Constructor.
void applyGain(FloatType *samples, int numSamples) noexcept
Applies a smoothed gain to a stream of samples S[i] *= gain.
void applyGain(AudioBuffer< FloatType > &buffer, int numSamples) noexcept
Applies a smoothed gain to a buffer.
void setCurrentAndTargetValue(FloatType newValue)
Sets the current value and the target value.
FloatType getTargetValue() const noexcept
Returns the target value towards which the smoothed value is currently moving.
FloatType getCurrentValue() const noexcept
Returns the current value of the ramp.
void applyGain(FloatType *samplesOut, const FloatType *samplesIn, int numSamples) noexcept
Computes output as a smoothed gain applied to a stream of samples.
A utility class for values that need smoothing to avoid audio glitches.
FloatType skip(int numSamples) noexcept
Skip the next numSamples samples.
FloatType getNextValue() noexcept
Compute the next value.
SmoothedValue(FloatType initialValue) noexcept
Constructor.
void reset(double sampleRate, double rampLengthInSeconds) noexcept
Reset to a new sample rate and ramp length.
SmoothedValue() noexcept
Constructor.
void reset(int numSteps) noexcept
Set a new ramp length directly in samples.
void setTargetValue(FloatType newValue) noexcept
Set the next value to ramp towards.
Used to indicate a linear smoothing between values.
Used to indicate a smoothing between multiplicative values.
constexpr bool approximatelyEqual(Type a, Type b, Tolerance< Type > tolerance=Tolerance< Type >{} .withAbsolute(std::numeric_limits< Type >::min()) .withRelative(std::numeric_limits< Type >::epsilon()))
Returns true if the two floating-point numbers are approximately equal.
RangedDirectoryIterator end(const RangedDirectoryIterator &)
Returns a default-constructed sentinel value.
Type unalignedPointerCast(void *ptr) noexcept
Casts a pointer to another type via void*, which suppresses the cast-align warning which sometimes ar...