39template <
typename SmoothedValueType>
44 template <
typename T>
struct FloatTypeHelper;
46 template <
template <
typename>
class SmoothedValueClass,
typename FloatType>
47 struct FloatTypeHelper <SmoothedValueClass <FloatType>>
49 using Type = FloatType;
52 template <
template <
typename,
typename>
class SmoothedValueClass,
typename FloatType,
typename SmoothingType>
53 struct FloatTypeHelper <SmoothedValueClass <FloatType, SmoothingType>>
55 using Type = FloatType;
59 using FloatType =
typename FloatTypeHelper<SmoothedValueType>::Type;
83 target = currentValue = newValue;
93 void applyGain (FloatType* samples,
int numSamples)
noexcept
95 jassert (numSamples >= 0);
99 for (
int i = 0; i < numSamples; ++i)
100 samples[i] *= getNextSmoothedValue();
114 void applyGain (FloatType* samplesOut,
const FloatType* samplesIn,
int numSamples)
noexcept
116 jassert (numSamples >= 0);
120 for (
int i = 0; i < numSamples; ++i)
121 samplesOut[i] = samplesIn[i] * getNextSmoothedValue();
132 jassert (numSamples >= 0);
136 if (buffer.getNumChannels() == 1)
138 auto* samples = buffer.getWritePointer (0);
140 for (
int i = 0; i < numSamples; ++i)
141 samples[i] *= getNextSmoothedValue();
145 for (
auto i = 0; i < numSamples; ++i)
147 auto gain = getNextSmoothedValue();
149 for (
int channel = 0; channel < buffer.getNumChannels(); channel++)
150 buffer.setSample (channel, i, buffer.getSample (channel, i) * gain);
156 buffer.applyGain (0, numSamples, target);
162 FloatType getNextSmoothedValue() noexcept
164 return static_cast <SmoothedValueType*
> (
this)->getNextValue();
169 FloatType currentValue = 0;
170 FloatType target = currentValue;
184namespace ValueSmoothingTypes
223template <
typename FloatType,
typename SmoothingType = ValueSmoothingTypes::Linear>
230 :
SmoothedValue ((FloatType) (std::is_same<SmoothingType, ValueSmoothingTypes::Linear>::value ? 0 : 1))
238 jassert (! (std::is_same<SmoothingType, ValueSmoothingTypes::Multiplicative>::value && initialValue == 0));
241 this->currentValue = initialValue;
242 this->target = this->currentValue;
250 void reset (
double sampleRate,
double rampLengthInSeconds)
noexcept
252 jassert (sampleRate > 0 && rampLengthInSeconds >= 0);
253 reset ((
int) std::floor (rampLengthInSeconds * sampleRate));
261 stepsToTarget = numSteps;
271 if (newValue == this->target)
274 if (stepsToTarget <= 0)
281 jassert (! (std::is_same<SmoothingType, ValueSmoothingTypes::Multiplicative>::value && newValue == 0));
283 this->target = newValue;
284 this->countdown = stepsToTarget;
303 this->currentValue = this->target;
305 return this->currentValue;
314 FloatType
skip (
int numSamples)
noexcept
316 if (numSamples >= this->countdown)
322 skipCurrentValue (numSamples);
324 this->countdown -= numSamples;
325 return this->currentValue;
339 JUCE_DEPRECATED_WITH_BODY (
void setValue (FloatType newValue,
bool force =
false)
noexcept,
352 template <typename T>
353 using LinearVoid = typename std::enable_if <std::is_same <T, ValueSmoothingTypes::Linear>::value, void>::type;
355 template <
typename T>
356 using MultiplicativeVoid =
typename std::enable_if <std::is_same <T, ValueSmoothingTypes::Multiplicative>::value,
void>::type;
359 template <
typename T = SmoothingType>
360 LinearVoid<T> setStepSize() noexcept
362 step = (this->target - this->currentValue) / (FloatType) this->countdown;
365 template <
typename T = SmoothingType>
366 MultiplicativeVoid<T> setStepSize()
368 step = std::exp ((std::log (std::abs (this->target)) - std::log (std::abs (this->currentValue))) / this->countdown);
372 template <
typename T = SmoothingType>
373 LinearVoid<T> setNextValue() noexcept
375 this->currentValue += step;
378 template <
typename T = SmoothingType>
379 MultiplicativeVoid<T> setNextValue() noexcept
381 this->currentValue *= step;
385 template <
typename T = SmoothingType>
386 LinearVoid<T> skipCurrentValue (
int numSamples)
noexcept
388 this->currentValue += step * (FloatType) numSamples;
391 template <
typename T = SmoothingType>
392 MultiplicativeVoid<T> skipCurrentValue (
int numSamples)
394 this->currentValue *= (FloatType) std::pow (step, numSamples);
398 FloatType step = FloatType();
399 int stepsToTarget = 0;
402template <
typename FloatType>
403using LinearSmoothedValue = SmoothedValue <FloatType, ValueSmoothingTypes::Linear>;
410template <
class SmoothedValueType>
411class CommonSmoothedValueTests :
public UnitTest
414 CommonSmoothedValueTests()
415 : UnitTest (
"CommonSmoothedValueTests",
"SmoothedValues")
418 void runTest()
override
420 beginTest (
"Initial state");
422 SmoothedValueType sv;
424 auto value = sv.getCurrentValue();
425 expectEquals (sv.getTargetValue(), value);
428 expectEquals (sv.getCurrentValue(), value);
429 expect (! sv.isSmoothing());
432 beginTest (
"Resetting");
434 auto initialValue = 15.0f;
436 SmoothedValueType sv (initialValue);
438 expectEquals (sv.getCurrentValue(), initialValue);
440 auto targetValue = initialValue + 1.0f;
441 sv.setTargetValue (targetValue);
442 expectEquals (sv.getTargetValue(), targetValue);
443 expectEquals (sv.getCurrentValue(), initialValue);
444 expect (sv.isSmoothing());
446 auto currentValue = sv.getNextValue();
447 expect (currentValue > initialValue);
448 expectEquals (sv.getCurrentValue(), currentValue);
449 expectEquals (sv.getTargetValue(), targetValue);
450 expect (sv.isSmoothing());
454 expectEquals (sv.getCurrentValue(), targetValue);
455 expectEquals (sv.getTargetValue(), targetValue);
456 expect (! sv.isSmoothing());
459 expectEquals (sv.getCurrentValue(), targetValue);
461 sv.setTargetValue (1.5f);
464 float newStart = 0.2f;
465 sv.setCurrentAndTargetValue (newStart);
466 expectEquals (sv.getNextValue(), newStart);
467 expectEquals (sv.getTargetValue(), newStart);
468 expectEquals (sv.getCurrentValue(), newStart);
469 expect (! sv.isSmoothing());
472 beginTest (
"Sample rate");
474 SmoothedValueType svSamples { 3.0f };
475 auto svTime = svSamples;
477 auto numSamples = 12;
479 svSamples.reset (numSamples);
480 svTime.reset (numSamples * 2, 1.0);
482 for (
int i = 0; i < numSamples; ++i)
485 expectWithinAbsoluteError (svSamples.getNextValue(),
486 svTime.getNextValue(),
491 beginTest (
"Block processing");
493 SmoothedValueType sv (1.0f);
496 sv.setTargetValue (2.0f);
498 const auto numSamples = 15;
500 AudioBuffer<float> referenceData (1, numSamples);
502 for (
int i = 0; i < numSamples; ++i)
503 referenceData.setSample (0, i, sv.getNextValue());
505 expect (referenceData.getSample (0, 0) > 0);
506 expect (referenceData.getSample (0, 10) < sv.getTargetValue());
507 expectWithinAbsoluteError (referenceData.getSample (0, 11),
511 auto getUnitData = [] (
int numSamplesToGenerate)
513 AudioBuffer<float> result (1, numSamplesToGenerate);
515 for (
int i = 0; i < numSamplesToGenerate; ++i)
516 result.setSample (0, i, 1.0f);
521 auto compareData = [
this](
const AudioBuffer<float>& test,
522 const AudioBuffer<float>& reference)
524 for (
int i = 0; i < test.getNumSamples(); ++i)
525 expectWithinAbsoluteError (test.getSample (0, i),
526 reference.getSample (0, i),
530 auto testData = getUnitData (numSamples);
531 sv.setCurrentAndTargetValue (1.0f);
532 sv.setTargetValue (2.0f);
533 sv.applyGain (testData.getWritePointer (0), numSamples);
534 compareData (testData, referenceData);
536 testData = getUnitData (numSamples);
537 AudioBuffer<float> destData (1, numSamples);
538 sv.setCurrentAndTargetValue (1.0f);
539 sv.setTargetValue (2.0f);
540 sv.applyGain (destData.getWritePointer (0),
541 testData.getReadPointer (0),
543 compareData (destData, referenceData);
544 compareData (testData, getUnitData (numSamples));
546 testData = getUnitData (numSamples);
547 sv.setCurrentAndTargetValue (1.0f);
548 sv.setTargetValue (2.0f);
549 sv.applyGain (testData, numSamples);
550 compareData (testData, referenceData);
555 SmoothedValueType sv;
558 sv.setCurrentAndTargetValue (1.0f);
559 sv.setTargetValue (2.0f);
561 Array<float> reference;
563 for (
int i = 0; i < 15; ++i)
564 reference.add (sv.getNextValue());
566 sv.setCurrentAndTargetValue (1.0f);
567 sv.setTargetValue (2.0f);
569 expectWithinAbsoluteError (sv.skip (1), reference[0], 1.0e-6f);
570 expectWithinAbsoluteError (sv.skip (1), reference[1], 1.0e-6f);
571 expectWithinAbsoluteError (sv.skip (2), reference[3], 1.0e-6f);
573 expectWithinAbsoluteError (sv.getCurrentValue(), reference[6], 1.0e-6f);
574 expectEquals (sv.skip (300), sv.getTargetValue());
575 expectEquals (sv.getCurrentValue(), sv.getTargetValue());
578 beginTest (
"Negative");
580 SmoothedValueType sv;
583 sv.reset (numValues);
585 std::vector<std::pair<float, float>> ranges = { { -1.0f, -2.0f },
586 { -100.0f, -3.0f } };
588 for (
auto range : ranges)
590 auto start = range.first, end = range.second;
592 sv.setCurrentAndTargetValue (start);
593 sv.setTargetValue (end);
595 auto val = sv.skip (numValues / 2);
598 expect (val > start && val < end);
600 expect (val < start && val > end);
602 auto nextVal = sv.getNextValue();
603 expect (end > start ? (nextVal > val) : (nextVal < val));
605 auto endVal = sv.skip (500);
606 expectEquals (endVal, end);
607 expectEquals (sv.getNextValue(), end);
608 expectEquals (sv.getCurrentValue(), end);
610 sv.setCurrentAndTargetValue (start);
611 sv.setTargetValue (end);
613 SmoothedValueType positiveSv { -start };
614 positiveSv.reset (numValues);
615 positiveSv.setTargetValue (-end);
617 for (
int i = 0; i < numValues + 2; ++i)
618 expectEquals (sv.getNextValue(), -positiveSv.getNextValue());
A multi-channel buffer containing floating point audio samples.
static void JUCE_CALLTYPE multiply(float *dest, const float *src, int numValues) noexcept
Multiplies the destination values by the source values.
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.
(void setValue(FloatType newValue, bool force=false) noexcept, { if(force) { this->setCurrentAndTargetValue(newValue);return;} setTargetValue(newValue);}) private typename std::enable_if< std::is_same< T, ValueSmoothingTypes::Multiplicative >::value, void >::type MultiplicativeVoid
THIS FUNCTION IS DEPRECATED.
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.