35template <
typename SmoothedValueType>
40 template <
typename T>
struct FloatTypeHelper;
42 template <
template <
typename>
class SmoothedValueClass,
typename FloatType>
43 struct FloatTypeHelper <SmoothedValueClass <FloatType>>
45 using Type = FloatType;
48 template <
template <
typename,
typename>
class SmoothedValueClass,
typename FloatType,
typename SmoothingType>
49 struct FloatTypeHelper <SmoothedValueClass <FloatType, SmoothingType>>
51 using Type = FloatType;
55 using FloatType =
typename FloatTypeHelper<SmoothedValueType>::Type;
77 target = currentValue = newValue;
87 void applyGain (FloatType* samples,
int numSamples)
noexcept
93 for (
int i = 0; i < numSamples; ++i)
94 samples[i] *= getNextSmoothedValue();
98 FloatVectorOperations::multiply (samples, target, numSamples);
108 void applyGain (FloatType* samplesOut,
const FloatType* samplesIn,
int numSamples)
noexcept
114 for (
int i = 0; i < numSamples; ++i)
115 samplesOut[i] = samplesIn[i] * getNextSmoothedValue();
119 FloatVectorOperations::multiply (samplesOut, samplesIn, target, numSamples);
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
158 return static_cast <SmoothedValueType*
> (
this)->getNextValue();
163 FloatType currentValue = 0;
164 FloatType target = currentValue;
178namespace ValueSmoothingTypes
225template <
typename FloatType,
typename SmoothingType = ValueSmoothingTypes::Linear>
232 :
SmoothedValue ((FloatType) (
std::is_same_v<SmoothingType, ValueSmoothingTypes::Linear> ? 0 : 1))
244 this->currentValue = initialValue;
245 this->target = this->currentValue;
253 void reset (
double sampleRate,
double rampLengthInSeconds)
noexcept
255 jassert (sampleRate > 0 && rampLengthInSeconds >= 0);
264 stepsToTarget = numSteps;
277 if (stepsToTarget <= 0)
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
362 step = (this->target - this->currentValue) / (FloatType) this->countdown;
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
376 this->currentValue += step;
380 this->currentValue *= step;
385 template <
typename T = SmoothingType>
386 void skipCurrentValue (
int numSamples)
noexcept
390 this->currentValue += step * (FloatType) numSamples;
394 this->currentValue *= (FloatType)
std::pow (step, numSamples);
399 FloatType step = FloatType();
400 int stepsToTarget = 0;
403template <
typename FloatType>
404using LinearSmoothedValue = SmoothedValue <FloatType, ValueSmoothingTypes::Linear>;
411template <
class SmoothedValueType>
412class CommonSmoothedValueTests :
public UnitTest
415 CommonSmoothedValueTests()
416 : UnitTest (
"CommonSmoothedValueTests", UnitTestCategories::smoothedValues)
419 void runTest()
override
421 beginTest (
"Initial state");
423 SmoothedValueType sv;
425 auto value = sv.getCurrentValue();
426 expectEquals (sv.getTargetValue(), value);
429 expectEquals (sv.getCurrentValue(), value);
430 expect (! sv.isSmoothing());
433 beginTest (
"Resetting");
435 auto initialValue = 15.0f;
437 SmoothedValueType sv (initialValue);
439 expectEquals (sv.getCurrentValue(), initialValue);
441 auto targetValue = initialValue + 1.0f;
442 sv.setTargetValue (targetValue);
443 expectEquals (sv.getTargetValue(), targetValue);
444 expectEquals (sv.getCurrentValue(), initialValue);
445 expect (sv.isSmoothing());
447 auto currentValue = sv.getNextValue();
448 expect (currentValue > initialValue);
449 expectEquals (sv.getCurrentValue(), currentValue);
450 expectEquals (sv.getTargetValue(), targetValue);
451 expect (sv.isSmoothing());
455 expectEquals (sv.getCurrentValue(), targetValue);
456 expectEquals (sv.getTargetValue(), targetValue);
457 expect (! sv.isSmoothing());
460 expectEquals (sv.getCurrentValue(), targetValue);
462 sv.setTargetValue (1.5f);
465 float newStart = 0.2f;
466 sv.setCurrentAndTargetValue (newStart);
467 expectEquals (sv.getNextValue(), newStart);
468 expectEquals (sv.getTargetValue(), newStart);
469 expectEquals (sv.getCurrentValue(), newStart);
470 expect (! sv.isSmoothing());
473 beginTest (
"Sample rate");
475 SmoothedValueType svSamples { 3.0f };
476 auto svTime = svSamples;
478 auto numSamples = 12;
480 svSamples.reset (numSamples);
481 svTime.reset (numSamples * 2, 1.0);
483 for (
int i = 0; i < numSamples; ++i)
486 expectWithinAbsoluteError (svSamples.getNextValue(),
487 svTime.getNextValue(),
492 beginTest (
"Block processing");
494 SmoothedValueType sv (1.0f);
497 sv.setTargetValue (2.0f);
499 const auto numSamples = 15;
501 AudioBuffer<float> referenceData (1, numSamples);
503 for (
int i = 0; i < numSamples; ++i)
504 referenceData.setSample (0, i, sv.getNextValue());
506 expect (referenceData.getSample (0, 0) > 0);
507 expect (referenceData.getSample (0, 10) < sv.getTargetValue());
508 expectWithinAbsoluteError (referenceData.getSample (0, 11),
512 auto getUnitData = [] (
int numSamplesToGenerate)
514 AudioBuffer<float> result (1, numSamplesToGenerate);
516 for (
int i = 0; i < numSamplesToGenerate; ++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);
535 compareData (testData, referenceData);
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),
542 testData.getReadPointer (0),
544 compareData (destData, referenceData);
545 compareData (testData, getUnitData (numSamples));
547 testData = getUnitData (numSamples);
548 sv.setCurrentAndTargetValue (1.0f);
549 sv.setTargetValue (2.0f);
550 sv.applyGain (testData, numSamples);
551 compareData (testData, referenceData);
556 SmoothedValueType sv;
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");
581 SmoothedValueType sv;
584 sv.reset (numValues);
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);
596 auto val = sv.skip (numValues / 2);
599 expect (val > start && val < end);
601 expect (val < start && val > end);
603 auto nextVal = sv.getNextValue();
604 expect (end > start ? (nextVal > val) : (nextVal < val));
606 auto endVal = sv.skip (500);
607 expectEquals (endVal, end);
608 expectEquals (sv.getNextValue(), end);
609 expectEquals (sv.getCurrentValue(), end);
611 sv.setCurrentAndTargetValue (start);
612 sv.setTargetValue (end);
614 SmoothedValueType positiveSv { -start };
615 positiveSv.reset (numValues);
616 positiveSv.setTargetValue (-end);
618 for (
int i = 0; i < numValues + 2; ++i)
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.