39 template <
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;
184 namespace ValueSmoothingTypes
231 template <
typename FloatType,
typename SmoothingType = ValueSmoothingTypes::Linear>
238 :
SmoothedValue ((FloatType) (std::is_same<SmoothingType, ValueSmoothingTypes::Linear>::value ? 0 : 1))
246 jassert (! (std::is_same<SmoothingType, ValueSmoothingTypes::Multiplicative>::value && initialValue == 0));
249 this->currentValue = initialValue;
250 this->target = this->currentValue;
258 void reset (
double sampleRate,
double rampLengthInSeconds) noexcept
260 jassert (sampleRate > 0 && rampLengthInSeconds >= 0);
261 reset ((
int) std::floor (rampLengthInSeconds * sampleRate));
269 stepsToTarget = numSteps;
279 if (newValue == this->target)
282 if (stepsToTarget <= 0)
289 jassert (! (std::is_same<SmoothingType, ValueSmoothingTypes::Multiplicative>::value && newValue == 0));
291 this->target = newValue;
292 this->countdown = stepsToTarget;
311 this->currentValue = this->target;
313 return this->currentValue;
322 FloatType
skip (
int numSamples) noexcept
324 if (numSamples >= this->countdown)
330 skipCurrentValue (numSamples);
332 this->countdown -= numSamples;
333 return this->currentValue;
347 JUCE_DEPRECATED_WITH_BODY (
void setValue (FloatType newValue,
bool force =
false) noexcept,
360 template <typename T>
361 using LinearVoid =
typename std::enable_if <std::is_same <T, ValueSmoothingTypes::Linear>::value,
void>::type;
363 template <
typename T>
364 using MultiplicativeVoid =
typename std::enable_if <std::is_same <T, ValueSmoothingTypes::Multiplicative>::value,
void>::type;
367 template <
typename T = SmoothingType>
368 LinearVoid<T> setStepSize() noexcept
370 step = (this->target - this->currentValue) / (FloatType) this->countdown;
373 template <
typename T = SmoothingType>
374 MultiplicativeVoid<T> setStepSize()
376 step = std::exp ((std::log (std::abs (this->target)) - std::log (std::abs (this->currentValue))) / this->countdown);
380 template <
typename T = SmoothingType>
381 LinearVoid<T> setNextValue() noexcept
383 this->currentValue += step;
386 template <
typename T = SmoothingType>
387 MultiplicativeVoid<T> setNextValue() noexcept
389 this->currentValue *= step;
393 template <
typename T = SmoothingType>
394 LinearVoid<T> skipCurrentValue (
int numSamples) noexcept
396 this->currentValue += step * (FloatType) numSamples;
399 template <
typename T = SmoothingType>
400 MultiplicativeVoid<T> skipCurrentValue (
int numSamples)
402 this->currentValue *= (FloatType) std::pow (step, numSamples);
406 FloatType step = FloatType();
407 int stepsToTarget = 0;
410 template <
typename FloatType>
411 using LinearSmoothedValue = SmoothedValue <FloatType, ValueSmoothingTypes::Linear>;
418 template <
class SmoothedValueType>
419 class CommonSmoothedValueTests :
public UnitTest
422 CommonSmoothedValueTests()
423 : UnitTest (
"CommonSmoothedValueTests", UnitTestCategories::smoothedValues)
426 void runTest()
override
428 beginTest (
"Initial state");
430 SmoothedValueType sv;
432 auto value = sv.getCurrentValue();
433 expectEquals (sv.getTargetValue(), value);
436 expectEquals (sv.getCurrentValue(), value);
437 expect (! sv.isSmoothing());
440 beginTest (
"Resetting");
442 auto initialValue = 15.0f;
444 SmoothedValueType sv (initialValue);
446 expectEquals (sv.getCurrentValue(), initialValue);
448 auto targetValue = initialValue + 1.0f;
449 sv.setTargetValue (targetValue);
450 expectEquals (sv.getTargetValue(), targetValue);
451 expectEquals (sv.getCurrentValue(), initialValue);
452 expect (sv.isSmoothing());
454 auto currentValue = sv.getNextValue();
455 expect (currentValue > initialValue);
456 expectEquals (sv.getCurrentValue(), currentValue);
457 expectEquals (sv.getTargetValue(), targetValue);
458 expect (sv.isSmoothing());
462 expectEquals (sv.getCurrentValue(), targetValue);
463 expectEquals (sv.getTargetValue(), targetValue);
464 expect (! sv.isSmoothing());
467 expectEquals (sv.getCurrentValue(), targetValue);
469 sv.setTargetValue (1.5f);
472 float newStart = 0.2f;
473 sv.setCurrentAndTargetValue (newStart);
474 expectEquals (sv.getNextValue(), newStart);
475 expectEquals (sv.getTargetValue(), newStart);
476 expectEquals (sv.getCurrentValue(), newStart);
477 expect (! sv.isSmoothing());
480 beginTest (
"Sample rate");
482 SmoothedValueType svSamples { 3.0f };
483 auto svTime = svSamples;
485 auto numSamples = 12;
487 svSamples.reset (numSamples);
488 svTime.reset (numSamples * 2, 1.0);
490 for (
int i = 0; i < numSamples; ++i)
493 expectWithinAbsoluteError (svSamples.getNextValue(),
494 svTime.getNextValue(),
499 beginTest (
"Block processing");
501 SmoothedValueType sv (1.0f);
504 sv.setTargetValue (2.0f);
506 const auto numSamples = 15;
508 AudioBuffer<float> referenceData (1, numSamples);
510 for (
int i = 0; i < numSamples; ++i)
511 referenceData.setSample (0, i, sv.getNextValue());
513 expect (referenceData.getSample (0, 0) > 0);
514 expect (referenceData.getSample (0, 10) < sv.getTargetValue());
515 expectWithinAbsoluteError (referenceData.getSample (0, 11),
519 auto getUnitData = [] (
int numSamplesToGenerate)
521 AudioBuffer<float> result (1, numSamplesToGenerate);
523 for (
int i = 0; i < numSamplesToGenerate; ++i)
524 result.setSample (0, i, 1.0f);
529 auto compareData = [
this](
const AudioBuffer<float>& test,
530 const AudioBuffer<float>& reference)
532 for (
int i = 0; i < test.getNumSamples(); ++i)
533 expectWithinAbsoluteError (test.getSample (0, i),
534 reference.getSample (0, i),
538 auto testData = getUnitData (numSamples);
539 sv.setCurrentAndTargetValue (1.0f);
540 sv.setTargetValue (2.0f);
541 sv.applyGain (testData.getWritePointer (0), numSamples);
542 compareData (testData, referenceData);
544 testData = getUnitData (numSamples);
545 AudioBuffer<float> destData (1, numSamples);
546 sv.setCurrentAndTargetValue (1.0f);
547 sv.setTargetValue (2.0f);
548 sv.applyGain (destData.getWritePointer (0),
549 testData.getReadPointer (0),
551 compareData (destData, referenceData);
552 compareData (testData, getUnitData (numSamples));
554 testData = getUnitData (numSamples);
555 sv.setCurrentAndTargetValue (1.0f);
556 sv.setTargetValue (2.0f);
557 sv.applyGain (testData, numSamples);
558 compareData (testData, referenceData);
563 SmoothedValueType sv;
566 sv.setCurrentAndTargetValue (1.0f);
567 sv.setTargetValue (2.0f);
569 Array<float> reference;
571 for (
int i = 0; i < 15; ++i)
572 reference.add (sv.getNextValue());
574 sv.setCurrentAndTargetValue (1.0f);
575 sv.setTargetValue (2.0f);
577 expectWithinAbsoluteError (sv.skip (1), reference[0], 1.0e-6f);
578 expectWithinAbsoluteError (sv.skip (1), reference[1], 1.0e-6f);
579 expectWithinAbsoluteError (sv.skip (2), reference[3], 1.0e-6f);
581 expectWithinAbsoluteError (sv.getCurrentValue(), reference[6], 1.0e-6f);
582 expectEquals (sv.skip (300), sv.getTargetValue());
583 expectEquals (sv.getCurrentValue(), sv.getTargetValue());
586 beginTest (
"Negative");
588 SmoothedValueType sv;
591 sv.reset (numValues);
593 std::vector<std::pair<float, float>> ranges = { { -1.0f, -2.0f },
594 { -100.0f, -3.0f } };
596 for (
auto range : ranges)
598 auto start = range.first, end = range.second;
600 sv.setCurrentAndTargetValue (start);
601 sv.setTargetValue (end);
603 auto val = sv.skip (numValues / 2);
606 expect (val > start && val < end);
608 expect (val < start && val > end);
610 auto nextVal = sv.getNextValue();
611 expect (end > start ? (nextVal > val) : (nextVal < val));
613 auto endVal = sv.skip (500);
614 expectEquals (endVal, end);
615 expectEquals (sv.getNextValue(), end);
616 expectEquals (sv.getCurrentValue(), end);
618 sv.setCurrentAndTargetValue (start);
619 sv.setTargetValue (end);
621 SmoothedValueType positiveSv { -start };
622 positiveSv.reset (numValues);
623 positiveSv.setTargetValue (-end);
625 for (
int i = 0; i < numValues + 2; ++i)
626 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.