VST3: fetch midi mappings all at once, use it for note/sound-off
[carla.git] / source / modules / juce_audio_basics / utilities / juce_SmoothedValue.h
blob21888b85094245da4fa75a722b285557f1e94eb8
1 /*
2 ==============================================================================
4 This file is part of the JUCE library.
5 Copyright (c) 2022 - Raw Material Software Limited
7 JUCE is an open source library subject to commercial or open-source
8 licensing.
10 The code included in this file is provided under the terms of the ISC license
11 http://www.isc.org/downloads/software-support-policy/isc-license. Permission
12 To use, copy, modify, and/or distribute this software for any purpose with or
13 without fee is hereby granted provided that the above copyright notice and
14 this permission notice appear in all copies.
16 JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
17 EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
18 DISCLAIMED.
20 ==============================================================================
23 namespace juce
26 //==============================================================================
27 /**
28 A base class for the smoothed value classes.
30 This class is used to provide common functionality to the SmoothedValue and
31 dsp::LogRampedValue classes.
33 @tags{Audio}
35 template <typename SmoothedValueType>
36 class SmoothedValueBase
38 private:
39 //==============================================================================
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;
54 public:
55 using FloatType = typename FloatTypeHelper<SmoothedValueType>::Type;
57 //==============================================================================
58 /** Constructor. */
59 SmoothedValueBase() = default;
61 //==============================================================================
62 /** Returns true if the current value is currently being interpolated. */
63 bool isSmoothing() const noexcept { return countdown > 0; }
65 /** Returns the current value of the ramp. */
66 FloatType getCurrentValue() const noexcept { return currentValue; }
68 //==============================================================================
69 /** Returns the target value towards which the smoothed value is currently moving. */
70 FloatType getTargetValue() const noexcept { return target; }
72 /** Sets the current value and the target value.
73 @param newValue the new value to take
75 void setCurrentAndTargetValue (FloatType newValue)
77 target = currentValue = newValue;
78 countdown = 0;
81 //==============================================================================
82 /** Applies a smoothed gain to a stream of samples
83 S[i] *= gain
84 @param samples Pointer to a raw array of samples
85 @param numSamples Length of array of samples
87 void applyGain (FloatType* samples, int numSamples) noexcept
89 jassert (numSamples >= 0);
91 if (isSmoothing())
93 for (int i = 0; i < numSamples; ++i)
94 samples[i] *= getNextSmoothedValue();
96 else
98 FloatVectorOperations::multiply (samples, target, numSamples);
102 /** Computes output as a smoothed gain applied to a stream of samples.
103 Sout[i] = Sin[i] * gain
104 @param samplesOut A pointer to a raw array of output samples
105 @param samplesIn A pointer to a raw array of input samples
106 @param numSamples The length of the array of samples
108 void applyGain (FloatType* samplesOut, const FloatType* samplesIn, int numSamples) noexcept
110 jassert (numSamples >= 0);
112 if (isSmoothing())
114 for (int i = 0; i < numSamples; ++i)
115 samplesOut[i] = samplesIn[i] * getNextSmoothedValue();
117 else
119 FloatVectorOperations::multiply (samplesOut, samplesIn, target, numSamples);
123 /** Applies a smoothed gain to a buffer */
124 void applyGain (AudioBuffer<FloatType>& buffer, int numSamples) noexcept
126 jassert (numSamples >= 0);
128 if (isSmoothing())
130 if (buffer.getNumChannels() == 1)
132 auto* samples = buffer.getWritePointer (0);
134 for (int i = 0; i < numSamples; ++i)
135 samples[i] *= getNextSmoothedValue();
137 else
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);
148 else
150 buffer.applyGain (0, numSamples, target);
154 private:
155 //==============================================================================
156 FloatType getNextSmoothedValue() noexcept
158 return static_cast <SmoothedValueType*> (this)->getNextValue();
161 protected:
162 //==============================================================================
163 FloatType currentValue = 0;
164 FloatType target = currentValue;
165 int countdown = 0;
168 //==============================================================================
170 A namespace containing a set of types used for specifying the smoothing
171 behaviour of the SmoothedValue class.
173 For example:
174 @code
175 SmoothedValue<float, ValueSmoothingTypes::Multiplicative> frequency (1.0f);
176 @endcode
178 namespace ValueSmoothingTypes
181 Used to indicate a linear smoothing between values.
183 @tags{Audio}
185 struct Linear {};
188 Used to indicate a smoothing between multiplicative values.
190 @tags{Audio}
192 struct Multiplicative {};
195 //==============================================================================
197 A utility class for values that need smoothing to avoid audio glitches.
199 A ValueSmoothingTypes::Linear template parameter selects linear smoothing,
200 which increments the SmoothedValue linearly towards its target value.
202 @code
203 SmoothedValue<float, ValueSmoothingTypes::Linear> yourSmoothedValue;
204 @endcode
206 A ValueSmoothingTypes::Multiplicative template parameter selects
207 multiplicative smoothing increments towards the target value.
209 @code
210 SmoothedValue<float, ValueSmoothingTypes::Multiplicative> yourSmoothedValue;
211 @endcode
213 Multiplicative smoothing is useful when you are dealing with
214 exponential/logarithmic values like volume in dB or frequency in Hz. For
215 example a 12 step ramp from 440.0 Hz (A4) to 880.0 Hz (A5) will increase the
216 frequency with an equal temperament tuning across the octave. A 10 step
217 smoothing from 1.0 (0 dB) to 3.16228 (10 dB) will increase the value in
218 increments of 1 dB.
220 Note that when you are using multiplicative smoothing you cannot ever reach a
221 target value of zero!
223 @tags{Audio}
225 template <typename FloatType, typename SmoothingType = ValueSmoothingTypes::Linear>
226 class SmoothedValue : public SmoothedValueBase <SmoothedValue <FloatType, SmoothingType>>
228 public:
229 //==============================================================================
230 /** Constructor. */
231 SmoothedValue() noexcept
232 : SmoothedValue ((FloatType) (std::is_same<SmoothingType, ValueSmoothingTypes::Linear>::value ? 0 : 1))
236 /** Constructor. */
237 SmoothedValue (FloatType initialValue) noexcept
239 // Multiplicative smoothed values cannot ever reach 0!
240 jassert (! (std::is_same<SmoothingType, ValueSmoothingTypes::Multiplicative>::value && initialValue == 0));
242 // Visual Studio can't handle base class initialisation with CRTP
243 this->currentValue = initialValue;
244 this->target = this->currentValue;
247 //==============================================================================
248 /** Reset to a new sample rate and ramp length.
249 @param sampleRate The sample rate
250 @param rampLengthInSeconds The duration of the ramp in seconds
252 void reset (double sampleRate, double rampLengthInSeconds) noexcept
254 jassert (sampleRate > 0 && rampLengthInSeconds >= 0);
255 reset ((int) std::floor (rampLengthInSeconds * sampleRate));
258 /** Set a new ramp length directly in samples.
259 @param numSteps The number of samples over which the ramp should be active
261 void reset (int numSteps) noexcept
263 stepsToTarget = numSteps;
264 this->setCurrentAndTargetValue (this->target);
267 //==============================================================================
268 /** Set the next value to ramp towards.
269 @param newValue The new target value
271 void setTargetValue (FloatType newValue) noexcept
273 if (newValue == this->target)
274 return;
276 if (stepsToTarget <= 0)
278 this->setCurrentAndTargetValue (newValue);
279 return;
282 // Multiplicative smoothed values cannot ever reach 0!
283 jassert (! (std::is_same<SmoothingType, ValueSmoothingTypes::Multiplicative>::value && newValue == 0));
285 this->target = newValue;
286 this->countdown = stepsToTarget;
288 setStepSize();
291 //==============================================================================
292 /** Compute the next value.
293 @returns Smoothed value
295 FloatType getNextValue() noexcept
297 if (! this->isSmoothing())
298 return this->target;
300 --(this->countdown);
302 if (this->isSmoothing())
303 setNextValue();
304 else
305 this->currentValue = this->target;
307 return this->currentValue;
310 //==============================================================================
311 /** Skip the next numSamples samples.
312 This is identical to calling getNextValue numSamples times. It returns
313 the new current value.
314 @see getNextValue
316 FloatType skip (int numSamples) noexcept
318 if (numSamples >= this->countdown)
320 this->setCurrentAndTargetValue (this->target);
321 return this->target;
324 skipCurrentValue (numSamples);
326 this->countdown -= numSamples;
327 return this->currentValue;
330 //==============================================================================
331 #ifndef DOXYGEN
332 /** Using the new methods:
334 lsv.setValue (x, false); -> lsv.setTargetValue (x);
335 lsv.setValue (x, true); -> lsv.setCurrentAndTargetValue (x);
337 @param newValue The new target value
338 @param force If true, the value will be set immediately, bypassing the ramp
340 [[deprecated ("Use setTargetValue and setCurrentAndTargetValue instead.")]]
341 void setValue (FloatType newValue, bool force = false) noexcept
343 if (force)
345 this->setCurrentAndTargetValue (newValue);
346 return;
349 setTargetValue (newValue);
351 #endif
353 private:
354 //==============================================================================
355 template <typename T>
356 using LinearVoid = typename std::enable_if <std::is_same <T, ValueSmoothingTypes::Linear>::value, void>::type;
358 template <typename T>
359 using MultiplicativeVoid = typename std::enable_if <std::is_same <T, ValueSmoothingTypes::Multiplicative>::value, void>::type;
361 //==============================================================================
362 template <typename T = SmoothingType>
363 LinearVoid<T> setStepSize() noexcept
365 step = (this->target - this->currentValue) / (FloatType) this->countdown;
368 template <typename T = SmoothingType>
369 MultiplicativeVoid<T> setStepSize()
371 step = std::exp ((std::log (std::abs (this->target)) - std::log (std::abs (this->currentValue))) / (FloatType) this->countdown);
374 //==============================================================================
375 template <typename T = SmoothingType>
376 LinearVoid<T> setNextValue() noexcept
378 this->currentValue += step;
381 template <typename T = SmoothingType>
382 MultiplicativeVoid<T> setNextValue() noexcept
384 this->currentValue *= step;
387 //==============================================================================
388 template <typename T = SmoothingType>
389 LinearVoid<T> skipCurrentValue (int numSamples) noexcept
391 this->currentValue += step * (FloatType) numSamples;
394 template <typename T = SmoothingType>
395 MultiplicativeVoid<T> skipCurrentValue (int numSamples)
397 this->currentValue *= (FloatType) std::pow (step, numSamples);
400 //==============================================================================
401 FloatType step = FloatType();
402 int stepsToTarget = 0;
405 template <typename FloatType>
406 using LinearSmoothedValue = SmoothedValue <FloatType, ValueSmoothingTypes::Linear>;
409 //==============================================================================
410 //==============================================================================
411 #if JUCE_UNIT_TESTS
413 template <class SmoothedValueType>
414 class CommonSmoothedValueTests : public UnitTest
416 public:
417 CommonSmoothedValueTests()
418 : UnitTest ("CommonSmoothedValueTests", UnitTestCategories::smoothedValues)
421 void runTest() override
423 beginTest ("Initial state");
425 SmoothedValueType sv;
427 auto value = sv.getCurrentValue();
428 expectEquals (sv.getTargetValue(), value);
430 sv.getNextValue();
431 expectEquals (sv.getCurrentValue(), value);
432 expect (! sv.isSmoothing());
435 beginTest ("Resetting");
437 auto initialValue = 15.0f;
439 SmoothedValueType sv (initialValue);
440 sv.reset (3);
441 expectEquals (sv.getCurrentValue(), initialValue);
443 auto targetValue = initialValue + 1.0f;
444 sv.setTargetValue (targetValue);
445 expectEquals (sv.getTargetValue(), targetValue);
446 expectEquals (sv.getCurrentValue(), initialValue);
447 expect (sv.isSmoothing());
449 auto currentValue = sv.getNextValue();
450 expect (currentValue > initialValue);
451 expectEquals (sv.getCurrentValue(), currentValue);
452 expectEquals (sv.getTargetValue(), targetValue);
453 expect (sv.isSmoothing());
455 sv.reset (5);
457 expectEquals (sv.getCurrentValue(), targetValue);
458 expectEquals (sv.getTargetValue(), targetValue);
459 expect (! sv.isSmoothing());
461 sv.getNextValue();
462 expectEquals (sv.getCurrentValue(), targetValue);
464 sv.setTargetValue (1.5f);
465 sv.getNextValue();
467 float newStart = 0.2f;
468 sv.setCurrentAndTargetValue (newStart);
469 expectEquals (sv.getNextValue(), newStart);
470 expectEquals (sv.getTargetValue(), newStart);
471 expectEquals (sv.getCurrentValue(), newStart);
472 expect (! sv.isSmoothing());
475 beginTest ("Sample rate");
477 SmoothedValueType svSamples { 3.0f };
478 auto svTime = svSamples;
480 auto numSamples = 12;
482 svSamples.reset (numSamples);
483 svTime.reset (numSamples * 2, 1.0);
485 for (int i = 0; i < numSamples; ++i)
487 svTime.skip (1);
488 expectWithinAbsoluteError (svSamples.getNextValue(),
489 svTime.getNextValue(),
490 1.0e-7f);
494 beginTest ("Block processing");
496 SmoothedValueType sv (1.0f);
498 sv.reset (12);
499 sv.setTargetValue (2.0f);
501 const auto numSamples = 15;
503 AudioBuffer<float> referenceData (1, numSamples);
505 for (int i = 0; i < numSamples; ++i)
506 referenceData.setSample (0, i, sv.getNextValue());
508 expect (referenceData.getSample (0, 0) > 0);
509 expect (referenceData.getSample (0, 10) < sv.getTargetValue());
510 expectWithinAbsoluteError (referenceData.getSample (0, 11),
511 sv.getTargetValue(),
512 2.0e-7f);
514 auto getUnitData = [] (int numSamplesToGenerate)
516 AudioBuffer<float> result (1, numSamplesToGenerate);
518 for (int i = 0; i < numSamplesToGenerate; ++i)
519 result.setSample (0, i, 1.0f);
521 return result;
524 auto compareData = [this] (const AudioBuffer<float>& test,
525 const AudioBuffer<float>& reference)
527 for (int i = 0; i < test.getNumSamples(); ++i)
528 expectWithinAbsoluteError (test.getSample (0, i),
529 reference.getSample (0, i),
530 2.0e-7f);
533 auto testData = getUnitData (numSamples);
534 sv.setCurrentAndTargetValue (1.0f);
535 sv.setTargetValue (2.0f);
536 sv.applyGain (testData.getWritePointer (0), numSamples);
537 compareData (testData, referenceData);
539 testData = getUnitData (numSamples);
540 AudioBuffer<float> destData (1, numSamples);
541 sv.setCurrentAndTargetValue (1.0f);
542 sv.setTargetValue (2.0f);
543 sv.applyGain (destData.getWritePointer (0),
544 testData.getReadPointer (0),
545 numSamples);
546 compareData (destData, referenceData);
547 compareData (testData, getUnitData (numSamples));
549 testData = getUnitData (numSamples);
550 sv.setCurrentAndTargetValue (1.0f);
551 sv.setTargetValue (2.0f);
552 sv.applyGain (testData, numSamples);
553 compareData (testData, referenceData);
556 beginTest ("Skip");
558 SmoothedValueType sv;
560 sv.reset (12);
561 sv.setCurrentAndTargetValue (1.0f);
562 sv.setTargetValue (2.0f);
564 Array<float> reference;
566 for (int i = 0; i < 15; ++i)
567 reference.add (sv.getNextValue());
569 sv.setCurrentAndTargetValue (1.0f);
570 sv.setTargetValue (2.0f);
572 expectWithinAbsoluteError (sv.skip (1), reference[0], 1.0e-6f);
573 expectWithinAbsoluteError (sv.skip (1), reference[1], 1.0e-6f);
574 expectWithinAbsoluteError (sv.skip (2), reference[3], 1.0e-6f);
575 sv.skip (3);
576 expectWithinAbsoluteError (sv.getCurrentValue(), reference[6], 1.0e-6f);
577 expectEquals (sv.skip (300), sv.getTargetValue());
578 expectEquals (sv.getCurrentValue(), sv.getTargetValue());
581 beginTest ("Negative");
583 SmoothedValueType sv;
585 auto numValues = 12;
586 sv.reset (numValues);
588 std::vector<std::pair<float, float>> ranges = { { -1.0f, -2.0f },
589 { -100.0f, -3.0f } };
591 for (auto range : ranges)
593 auto start = range.first, end = range.second;
595 sv.setCurrentAndTargetValue (start);
596 sv.setTargetValue (end);
598 auto val = sv.skip (numValues / 2);
600 if (end > start)
601 expect (val > start && val < end);
602 else
603 expect (val < start && val > end);
605 auto nextVal = sv.getNextValue();
606 expect (end > start ? (nextVal > val) : (nextVal < val));
608 auto endVal = sv.skip (500);
609 expectEquals (endVal, end);
610 expectEquals (sv.getNextValue(), end);
611 expectEquals (sv.getCurrentValue(), end);
613 sv.setCurrentAndTargetValue (start);
614 sv.setTargetValue (end);
616 SmoothedValueType positiveSv { -start };
617 positiveSv.reset (numValues);
618 positiveSv.setTargetValue (-end);
620 for (int i = 0; i < numValues + 2; ++i)
621 expectEquals (sv.getNextValue(), -positiveSv.getNextValue());
627 #endif
629 } // namespace juce