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,
355 setTargetValue (newValue);
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>
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>
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>
402 this->currentValue *= (FloatType) std::pow (step, numSamples);
406 FloatType step = FloatType();
407 int stepsToTarget = 0;
410 template <
typename FloatType>
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;
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)
523 for (
int i = 0; i < numSamplesToGenerate; ++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);
546 sv.setCurrentAndTargetValue (1.0f);
547 sv.setTargetValue (2.0f);
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);
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());
FloatType getNextValue() noexcept
Compute the next value.
FloatType getTargetValue() const noexcept
Returns the target value towards which the smoothed value is currently moving.
Used to indicate a linear smoothing between values.
A utility class for values that need smoothing to avoid audio glitches.
FloatType getCurrentValue() const noexcept
Returns the current value of the ramp.
void reset(int numSteps) noexcept
Set a new ramp length directly in samples.
FloatType skip(int numSamples) noexcept
Skip the next numSamples samples.
void add(const ElementType &newElement)
Appends a new element at the end of the array.
void reset(double sampleRate, double rampLengthInSeconds) noexcept
Reset to a new sample rate and ramp length.
Type getSample(int channel, int sampleIndex) const noexcept
Returns a sample from the buffer.
static void JUCE_CALLTYPE multiply(float *dest, const float *src, int numValues) noexcept
Multiplies the destination values by the source values.
SmoothedValueBase()=default
Constructor.
bool isSmoothing() const noexcept
Returns true if the current value is currently being interpolated.
This is a base class for classes that perform a unit test.
A multi-channel buffer containing floating point audio samples.
Used to indicate a smoothing between multiplicative values.
A base class for the smoothed value classes.
SmoothedValue() noexcept
Constructor.
Holds a resizable array of primitive or copy-by-value objects.
SmoothedValue(FloatType initialValue) noexcept
Constructor.
void applyGain(AudioBuffer< FloatType > &buffer, int numSamples) noexcept
Applies a smoothed gain to a buffer.
void setTargetValue(FloatType newValue) noexcept
Set the next value to ramp towards.
void setCurrentAndTargetValue(FloatType newValue)
Sets the current value and the target value.
Type * getWritePointer(int channelNumber) noexcept
Returns a writeable pointer to one of the buffer's channels.
void setSample(int destChannel, int destSample, Type newValue) noexcept
Sets a sample in the buffer.
int getNumSamples() const noexcept
Returns the number of samples allocated in each of the buffer's channels.
void applyGain(FloatType *samplesOut, const FloatType *samplesIn, int numSamples) noexcept
Computes output as a smoothed gain applied to a stream of samples.
void applyGain(FloatType *samples, int numSamples) noexcept
Applies a smoothed gain to a stream of samples S[i] *= gain.
(void setValue(Type 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.