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
89 jassert (numSamples >= 0);
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
110 jassert (numSamples >= 0);
114 for (
int i = 0; i < numSamples; ++i)
115 samplesOut[i] = samplesIn[i] * getNextSmoothedValue();
119 FloatVectorOperations::multiply (samplesOut, samplesIn, target, numSamples);
126 jassert (numSamples >= 0);
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;
225template <
typename FloatType,
typename SmoothingType = ValueSmoothingTypes::Linear>
240 jassert (! (std::is_same_v<SmoothingType, ValueSmoothingTypes::Multiplicative>
241 && approximatelyEqual (initialValue, (FloatType) 0)));
244 this->currentValue = initialValue;
245 this->target = this->currentValue;
253 void reset (
double sampleRate,
double rampLengthInSeconds)
noexcept
255 jassert (sampleRate > 0 && rampLengthInSeconds >= 0);
256 reset ((
int) std::floor (rampLengthInSeconds * sampleRate));
264 stepsToTarget = numSteps;
274 if (approximatelyEqual (newValue, this->target))
277 if (stepsToTarget <= 0)
284 jassert (! (std::is_same_v<SmoothingType, ValueSmoothingTypes::Multiplicative>
285 && approximatelyEqual (newValue, (FloatType) 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
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>
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);
586 std::vector<std::pair<float, float>> ranges = { { -1.0f, -2.0f },
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());
bool isSmoothing() const noexcept
SmoothedValueBase()=default
void applyGain(FloatType *samples, int numSamples) noexcept
void applyGain(AudioBuffer< FloatType > &buffer, int numSamples) noexcept
void setCurrentAndTargetValue(FloatType newValue)
FloatType getTargetValue() const noexcept
FloatType getCurrentValue() const noexcept
void applyGain(FloatType *samplesOut, const FloatType *samplesIn, int numSamples) noexcept
FloatType skip(int numSamples) noexcept
FloatType getNextValue() noexcept
void setValue(FloatType newValue, bool force=false) noexcept
SmoothedValue(FloatType initialValue) noexcept
void reset(double sampleRate, double rampLengthInSeconds) noexcept
void reset(int numSteps) noexcept
void setTargetValue(FloatType newValue) noexcept